Introduction
Angular.js, a JavaScript library and framework created in Google, is a fresh view into building great web applications. You can read a lot of articles on how it separates the concerns of the application, improves testability, and keeps to web app best practices, but I want to highlight a feature that is not shown off as regularly, extending the document object model API.
What you will learn
- Angular template basics
- Template secret sauce
- Domain specific syntaxs
What you should know
- Basics of HTML
- JavaScript basics
Let's jump right into it and build a twitter timeline user
interface. I would like to show my recent tweets and allow the user to click on
any tweet and highlight it.
Template 1. Angular Template
<ul>
<li ng-repeat="tweet in tweets" class="tweet {{tweet.highlight}}" ng-click="selectTweet(tweet)">
<h4>{{tweet.text}}</h4>
<ul ng-show="tweet.entities.hashtag.length == 0">
<li ng-repeat="hashtag in tweet.entities.hashtag">{{hashtag.text}}</li>
</ul>
<span ng-repeat="user in tweet.contributors">
<span>@{{user.screen_name}}</span>
</span>
</li>
</ul>
Here we have an example angular template, to show how
flexible your template can be. There are a couple things going on here so I
will break it down.
<li ng-repeat="hashtag in tweet.entities.hashtag">{{hashtag.text}}</li>
The directive 'ng-repeat' is used a couple places in this
example because it is very powerful. It both repeats the element for each in an
array and adds that object to the scope of the element. It does not change the
scope, which is an important distinction from other template engines like
handlebars.js where '../' is needed to access values outside of the object
scope. Angular does use the mustache syntax of {{}} to insert values.
<ul ng-show="tweet.entities.hashtag.length == 0">
The directive 'ng-show
' tells angular to show or hide
depending on the statement you pass in. There are a variety of statements you
can use here, so it adds a good amount of flexibility. Its important to note
here, we are not giving this directive JavaScript, but a string that is parsed
by angular. '==
' is not the same as '==
' in JavaScript and acts more like
'===
'.
<li ng-repeat="tweet in tweets" class="tweet {{tweet.highlight}}" ng-click="selectTweet(tweet)">
'ng-click
' shows off something else that is different about
angular, there is direct binding to the controller scope. Let me show off what
the controller looks like.
Controller 1. Angular Controller
function($scope) {
$scope.tweets = Twitter.getTimeline('1950787875');
$scope.selectTweet = function(tweet) {
if(tweet.highlight === 'highlight') {
tweet.highlight = '';
} else {
tweet.highlight = 'highlight';
}
};
};
As you can see, when angular calls the controller function
it is going to pass the scope into the controller function. Scope is the object
that connects the view and the controller. Let's pretend in this example that we
have a global object Twitter that has a method getTimeline
that returns an
array of tweets when called with a user id. When we set 'tweets', angular will
use that to render the template into the DOM. 'ng-click
' will link the click
event on that element to the function selecTweet
in the scope, allowing you to
also pass arguments. It's important to note here also, what you pass to
'ng-click
' is not run directly in JavaScript, but parsed by angular. Don't let
that trip you up.
This shows off the basics but we haven't gotten into the
power of angular templates. Angular is designed to build a domain specific
language. This allows you to effectively extend the DOM just for your app. Let
me show you what that template will look like.
Template 2. Angular Template with Custom Directives
<ul>
<li ng-repeat="tweet in tweets" class="tweet {{tweet.highlight}}" ng-click="selectTweet(tweet)">
<h4>{{tweet.text}}</h4>
<entities ng-model="tweet.entities"></entities>
<div ng-repeat="user in tweet.contributors">
<user ng-model="user"></user>
</div>
</li>
</ul>
If you know your html you will notice quickly that
<entities>
and <user>
are not HTML elements. These are angular
elements that are created for this application. <entities>
takes a list
of entities passed in by 'ng-model
', and renders them. <user>
does a
similar thing with a user object. This cleans up our main template here, and
makes the template extendable and reusable. Both entities and user now have
their own templates.
<ul ng-show="hashtags.length == 0">
<li ng-repeat="tag in hashtags">{{tag.text}}</li>
</ul>
Here is the entities template. As you can see, the template
is cleaner here, but also we can now user entities in other locations in our
angular app. Once we put the <entities>
tag all over our app making
changes is simple. The secret sauce of angular I am showing off here is
directives. Directives are a collection of functionality that can be added to
any element in the DOM and show up in a couple different ways. I just showed
you 'ng-*
' and '<*></*>
'. These two act differently, one adding on
functionality to an element and another creating a new element, but they are
based around the same idea.
Right out of the box we get a quick list of tweets presented
in a way we are used to, <li>
elements. If we need more power, we can
build our own elements through domain specific directives. We have the ability
to quickly add event listeners on these elements, and bind model values to
element text.
I'm not arguing that you cannot do this with other
frameworks but with angular.js you have a set of tools that take the DOM and
extends it to make dynamic applications simple to write and understand. Add in
support from Google and a growing, smart community of developers and you have a
great framework that every JavaScript developer needs to try.