English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

AngularJS Book

Chapter 9. Practical Applications

From here on we'll present more examples of AngularJS in the real world. We'll find some common problems and present solutions the AngularJS way. Some will be very basic and others will be more complex.

Ajax

AngularJS has two services for handling Ajax requests. The first, $resource, is highly abstracted and tailor-made for RESTful applications. The second, $http, is lower-level and will do just about anything. The general recommendation is to use $resource (don't forget to include the ngResource module!) if your REST API is simple.

$resource Service

Let's say we have a REST API.  Doing a GET request against /donut/foo will return you the Donut object with ID "foo".  POSTing to this URL will "save" the Donut object.  DELETE-ing to this URL will delete.  A GET request to /donuts/ will give you all of the Donut objects.  Here's an example of doing some operations against this backend returning JSON, using the $resource service from the ngResource module:

// $resource returns a Resource object. The second parameter describes the parameters to the resource call.
// Here, specify a :id in the URL and tell AngularJS to insert the value of the 'donutId' string in the
// URL when making a request. This assumes that the server will return a collection of donuts at the "/donut/" URL,
// and if an ID is specified ("/donut/foo") then it will only return that particular donut.
var Donut = $resource('/donut/:id', {id: '@donutId'});
// use this object to query. Note that powdered_donut is immediately undefined after this line executes, because
// "get" is an asynchronous operation. Some magic happens, however, and when the item is retrieved from the server,
// the value of powdered_donut is populated.
// Functions named Donut.foo() are "class" functions, and the returned value of these functions
// is considered an instance of Donut. Donut.save() and donut.$save() do basically the same thing; the former requires
// more parameters.
// this is an HTTP GET.
var powdered_donut = Donut.get({donutId: 'powdered'}, function(donut) {
// optional success callback; do stuff with donut. Will have the same value as powdered_donut when this resolves.
// this
powdered_donut.sold_out = donut;
powdered_donut.$save(); // HTTP POST; note that AngularJS' prefixing with "$" is to avoid clobbering your namespace

// or you could just HTTP DELETE the donut
// powdered_donut.$delete();
}, function(error) {
// optional failure callback
});

// this is an HTTP DELETE to /donut/glazed.
// if we had retrieved the glazed donut using get(), then we could delete it using the $delete() function. Same thing.
Donut.delete({donutId: 'glazed'}, function(donut) {
// optional callback if we successfully deleted
}, function(error) {
// optional failure callback
});
// get all donuts. HTTP GET; query returns a promise that will resolve to the same value as the donut parameter.
var all_donuts = Donut.query(function(donuts) {
// optional success callback
}, function(error) {
/ / optional failure callback
});
// HTTP POST. can use $save on actual
Donut.save({donutId: 'pepto'}, {id: 'pepto', cost: '1.23'}, function(donut) {
// successful save
}, function(error) {
// unsuccessful save
});


 But Donut.get() just returns undefined and I want to do something with the result! Can I make $resource synchronous?

No. You cannot make $resource synchronous. Use the "success" callback function to do whatever you want to with the result of Donut.get(). If it bothers you that much, you don't have to do this:

$scope.foo = Foo.get({bar: 'baz'});

You can do this instead:

Foo.get({bar: 'baz'}, function(foo) {
$scope.foo = foo;
});

They do the same thing. This is almost always confusing to those new to AngularJS. In the end it simply provides you some syntactic sugar.



$http Service

If you don't have a RESTful server you're talking to, you are going to use the $http service. Think of this as akin to jQuery's $.ajax function. You can initiate a HTTP GET request like so:

$http({
method: 'GET',
url: '/pigs/and/chickens/',
data: {
cows: 'horses'
},
params: {
sheepdogs: 'mules'
},
headers: {
'X-Farm-Animals': 1.0
}
}).success(function(data) {
// do stuff with response data; AngularJS will try to give you a JS object if it detects JSON
}).error(function(data) {
// handle non-200 response codes
});

This will make a HTTP GET request to /pigs/and/chickens/?sheepdogs=mules with request payload {"cows": "horses"}, with special header X-Farm-Animals. After the request has completed, the data will be passed to a success or error callback, depending on what the server responded with.

There is some sugar in place to make these calls a little less unwieldy; an identical GET request can be made this way:

$http.get('/pigs/and/chickens', {cows: 'horses'}, {
params: {
sheepdogs: 'mules'
},
headers: {
'X-Farm-Animals': 1.0
}
}).success(function(data) {
// do stuff with response data; AngularJS will try to give you a JS object if it detects JSON
}).error(function(data) {
// handle non-200 response codes
});

You can see in the Posts service that we have a function, getPosts, which calls $http like so:

blog.service('Posts', function ($http) {
this.getPosts = function () {
return $http.get('data.json');
};
});

Calling Posts.getPosts() will make a simple call to the file at the data.json URL. The function returns the value of $http.get(), which is not the data it retrieved; it returns a promise. We then can take the result of $http.get() and use it like we would any promise:

Posts.getPosts().success(function (data) {
// do stuff
});

Note that vanilla promises do not supply the success or error methods; they only supply a then method, which works similarly. The difference between the two is that success and error are both passed response data, while the then is passed the entire response object (note that then will accept two functions as parameters; the first for success/"resolved" and the second for failure/"rejected"). This object contains headers and other information that you may or may not care about.

Custom Headers

If you need every HTTP request to send a custom header to the server, you do not have to pass the headers object to each call to $http; instead, we can configure the $httpBackend to do this automatically during the system's initialization phase.

Say our blog service needs an X-Blog-Version header sent with each request, or different things depending if the request is a GET or POST. This is how to set that up:

// configure $http to always pass these headers

blog.config(function($httpProvider) {
$httpProvider.defaults.headers.common['X-Blog-Version'] = '1.0';
$httpProvider.defaults.headers.post['X-I-Am-A-POST-Request'] = true;
$httpProvider.defaults.headers.get['X-I-Am-A-GET-Request'] = true;
});

Note that in the config function you will inject a $httpProvider. This essentially allows you to pre-configure the $http service before using it.  For more information, consult the Services chapter.

If you need to change the default headers on-the-fly, the $http module allows you to monkey with them the same way:

blog.run(function($http) {
$http.defaults.headers.common['X-Blog-Version'] = '1.0';
// etc
});

Use the first example if you are sure all your HTTP requests will need a particular header; only use the second if you absolutely have to.

Unit Testing $http

You don't want your unit tests making $http calls unless you are writing functional/E2E tests, which we are not. The authors have provided a nice module to help you with this called ngMock. Include the angular-mocks.js file in your tests, and everything will get set up automatically.

Let's test our Posts service. We need to tell the mock $httpBackend service to expect a call to $http, what the call should return, and finally to flush all the requests. It looks like this:

describe('Blog Services', function () {
var Posts, httpBackend;

beforeEach(inject(function ($injector, $httpBackend) {
Posts = $injector.get('Posts');
httpBackend = $httpBackend;
}));

describe('Posts', function () {
it('should initiate the http call', function () {
var promise;
httpBackend.expectGET('data.json').respond('foo');
promise = Posts.getPosts();
expect(angular.isFunction(promise.then)).toBeTruthy();
promise.success(function (data) {
expect(data).toBe('foo');
});
promise.then(function (res) {
expect(res.data).toBe('foo');
expect(res.status).toBe(200);
expect(res.config).toEqual({
method: 'GET',
url: 'data.json'
});
expect(angular.isFunction(res.headers)).toBeTruthy();
});
httpBackend.flush();
});
});
});

For fun, we tested the success and then methods of our promise object returned by the $http call, but we only really care about the data coming out of the request (which is "foo" in both cases).

There are expectFOO methods supplied for every HTTP request type. Note that these functions are expectations; if the call does not get made by the code, the expectation will fail, an exception will be thrown, and your test will not pass. If, for some reason, a request may be made, use the when method: httpBackend.whenGET('data.json').respond('foo'). If a request is made, this will handle it, but if a request is not made, no exception is thrown. Typically you will want to use the expect methods when writing unit tests, but if you are writing functional/E2E tests, then you will want to use the when methods, since checking if a request completed is beyond the scope of a functional test.

But wait, there's more. You may need to unit test code that calls code that uses $http! Here's an example unit test that asserts the controller calls Posts.getPosts():

it('should call Posts.getPosts() and execute a function upon resolving', function() {
inject(function(Posts, Post, $q) {
var dfrd = $q.defer(),
success_spy = jasmine.createSpy('success').andReturn([]);
angular.extend(dfrd.promise, {
success: success_spy
});
spyOn(Posts, 'getPosts').andReturn(dfrd.promise);
$controller('ContentCtrl', {$scope: scope, Posts: Posts,
Post: Post, $routeParams: {}});
expect(Posts.getPosts).toHaveBeenCalled();
expect(dfrd.promise.success).toHaveBeenCalled();
});
});

Instead of letting Posts.getPosts() actually try to hit the server, we create a spy instead, and assert that the method was called. Spies are incredibly handy to ensure your unit tests are more unit-y. Now we don't care what Posts.getPosts() actually does, because we don't actually call it. Do be aware that if you are using success(), you will have to extend any promise fixture with this function, as it is not there by default.

Different Data Types

AngularJS will attempt to convert to and from JSON for you. If it finds you have given it an object to send to the server, it will serialize this into JSON and send it away. If it retrieves a request that can be parsed into JSON, it will give you the resulting JavaScript object back.

If you are not working in JSON, simply pass a string as the data. Maybe you are working with XML and the server expects it and sends it back. For example:

$http.get('/some/xml/service', '<foo><bar>baz</bar></foo>')
.success(function(data) {
// data is now an XML string.
// parse your XML here and wonder why you are not working with JSON
});

This covers JSON and XML (or any plain string data). What if your server wants x-www-form-urlencoded data? The JSFiddle (http://jsfiddle.net) /echo service is a good example of this. Here's a working example:

$http.post('/echo/json/', 'json=' + encodeURIComponent(angular.toJson({foo: 'bar'})), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}).success(function(data) {
$scope.data = data; // puts {foo: 'bar'} in $scope.data
});

That's a handy trick, but here's another: you may want to fake a form submit. I've written a handy little factory that converts a (flat!) object to multipart/form-data for you:

myApp.factory('formDataObject', function () {
return function (data) {
var fd = new FormData(), key;
for (key in data) {
if (data.hasOwnProperty(key)) {
fd.append(key, data[key]);
}
}
return fd;
};
});

To use this factory, just inject formDataObject into whatever is calling $http, and do this:

// note not using "post" method here; this one seems to be a bit more relaxed on the headers
// but it may work for you
$http({
method: 'POST',
url: '/maybe/accepts/a/file',
data: flat_object,
// XXX: this seems to be a bug in AngularJS
headers: {
'Content-type': '',
'Content-Type': ''
},
transformRequest: formDataObject
}).success(function(data) {
// process response
});

You can easily grab the information from an <input type="file"/> this way and upload via XHR, by accessing the HTMLElement's files array, once a file or files has been chosen.  Once you have something from the files array, pass it as the data as in the above example.  The formDataObject factory will do the work for you to convert it into something the server would expect.  Note that AngularJS, while it has implemented many input directives, has not implemented the file type input.  So sticking an ngModel on it would not work, whatever you think working might be.

Spinners And The Like

Fair warning: there should be a better way to do the following. Currently AngularJS supports response interceptors (which we'll cover in a minute), but not request interceptors, so while those would be a better fit for this kind of solution, it's not possible given that we could use them to hide a spinner, but not show one--that's where a request interceptor would come in.

You may want to display an animated spinner while Ajax requests are processing. This is easy to do across all of your requests by using a request transformation. Request transformations are really for actually taking some request-data-to-be and modifying it to your liking before it gets sent. However, you can just as easily set up any kind of action to be taken before any request is made. Let's do this in our blog by modifying our config() call.

// configure $http to always pass this header
// also, display a spinner during any Ajax request
blog.config(function ($httpProvider) {
var spinner; // jqLite obj representing spinner
$httpProvider.defaults.headers.common['X-Blog-Version'] = '1.0';
// first thing we do when sending a request: pop a spinner
$httpProvider.defaults.transformRequest.unshift(function (req) {
spinner = angular.element('<h1>Working...</h1>');
$('body').append(spinner);
return req; // return request unspoiled
});
// last thing we do when retrieving a response (before success() and the like):
// hide the spinner.
$httpProvider.defaults.transformResponse.push(function (res) {
if (spinner) {
spinner.remove();
}
return res; // return response unspoiled
});
});

This is very basic, and you could wind up in a situation where two spinners are displayed at once, but this is likely a rare occurrence.

If you want to disable this behavior for certain requests (continuous polling, for example), you can simply override it to do nothing:

$http.get('/some/url', {transformRequest: angular.noop});

Mind you this is a hack. This is not for what request transforms and response transforms were intended to do. Maybe better to decorate the $httpBackend service. This service sits between $http and XHR and handles all outgoing requests.

blog.config(function ($httpProvider, $routeProvider, $provide) {
.. snip ..
// pop a spinner when any HTTP req is made and remove it when the response comes in
$provide.decorator('$httpBackend', function ($delegate) {
// do not blast mock $httpBackend if it exists
if (angular.isDefined(angular.mock)) {
return $delegate;
}
return function (method, url, reqData, done, reqHeaders, timeout, withCredentials) {
var spinner = angular.element('<h1>Working...</h1>');
$('body').append(spinner);
$delegate(method, url, reqData, function () {
spinner.remove();
done.apply(this, arguments);
}, reqHeaders, timeout, withCredentials);
};
});
);

Note we've injected the $provide service--don't forget that. A decorator will be called any time a particular service (in this case, $httpBackend is injected). This decorator simply pops a spinner, intercepts the done method passed to $httpBackend, then removes the spinner. The documentation for $httpBackend is sparse/non-existent; to figure this out, I had to use the force and read the source.

Displaying Ajax Errors

If your server is halfway sane it will give you a 404 when you expect a 404. It will give you a 500 when something else goes awry. Let's assume it does.

We can show an error message every time we get a non-OK response from the server using a response interceptor. Append the following to our config() call:

// error messaging upon non-OK response from server
$httpProvider.responseInterceptors.push(function ($q, $window) {
return function (promise) {
return promise.then(angular.identity, function(res) {
$window.alert('Egad, there was an error!');
return $q.reject(res);
});
};
});

The promise passed into the first function is the promise from your $http call. The promise would be rejected, because $http did not make a successful request, thus executing the second parameter of the call to then(), above (the first parameter, angular.identity, is simply a function that returns its first argument). Next, we pass the rejection down the line (with the response information, of course) to be handled by error() or any second parameter of a then() attached to the original promise returned by the $http call in your code. To test this, simply change the URL in the getPosts() function of the Posts service to be something non-existent.

If your server responds with JSON objects that have errors in them, we can handle that too. Even if we are getting a 200 from the server, the request may have thrown an internal exception, and we can look for that. For example, there may be an $error key in the response JSON. Every time we see that, we know something went south on the server side. Let's replace our code above with the following:

// error messaging upon non-OK response from server
// or special $error response
$httpProvider.responseInterceptors.push(function ($q, $window) {
return function (promise) {
return promise.then(function(res) {
if (res.data.$error) {
$window.alert('Yikes, the server had a problem!');
return $q.reject(res);
}
return res;
}, function(res) {
$window.alert('Egad, there was an error!');
return $q.reject(res);
});
};
});

Now, any time any response from the server has $error as a top-level key in its JSON, you'll pop an alert and the failure() function will be executed, just like any 500 or 404 error.

Routing

It's often desirable to have the hash portion of a URL direct the user to a particular view. AngularJS provides a service for this called $route. During the config() phase, you can configure $routeProvider to point to the correct view when coupled with the ngView directive. Furthermore, you can pass data through the URL and retrieve it using the $routeParams service.

Be careful when using named anchor tags and the $route service. They both use the "hash" portion of the URL to do different things. The $route service will take precedence over any fragment intended to hit a named anchor--if you have a route called "foo" and an anchor named "foo", AngularJS will attempt to execute the route when visiting http://your.com/#foo.

 


 Can I use more than one ngView directive at once?


No. ngView can only be included once per HTML page. If you are switching views when using the $route service, plan ahead for the best place to use ngView which will include the proper templates. Watch for this functionality in the future.



Configuration

Let's setup some routing in our blog. Say we want to load a particular post based on its ID. First, we must set up our HTML to handle routing. Note that this requires an ngView directive.

<!DOCTYPE html>
<html ng-app="blog">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title ng-bind-template="{{title}}"></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min.js" type="text/javascript"
charset="utf-8"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"
charset="utf-8"></script>
<script src="blog.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div ng-controller="HeadCtrl">
<h1>{{title}}</h1>
<h3>{{description}}</h3>
</div>
<div ng-controller="ContentCtrl">
<new-post-area post="new_post" cancel-text="Cancel" save-text="Save" cancel="cancel" save="save"></new-post-area>
<div id="posts-form">
Order By: <select ng-model="order"><option value="title">Title</option><option value="date">Date</option></select>
<input type="checkbox" ng-model="descending"/> Descending <br/>
Search: <input type="text" ng-model="filter"/>
</div>

<ng-view></ng-view>
</div>
<!-- directive: copyright -->
----

Past this point, we have AngularJS templates defined within <script> tags.

<script type="text/ng-template" id="posts">
<div id="{{post.id}}" ng-repeat="post in posts|orderBy:order:descending|filter:filter" ng-class-even="'even'" ng-class-odd="'odd'">
<ng-include src="'post'"></ng-include>
</div>
</script>

<script type="text/ng-template" id="post">
<ng-switch on="post.editing">
<div ng-switch-when="true">
<div class="important-background-color" color="#EEE">
<input type="text" ng-model="post.temp.title"/><br/>
<textarea ng-model="post.temp.body"></textarea><br/>
<button ng-click="cancel(post)">Cancel</button>
<button ng-click="save(post)">Save Changes</button>
</div>
</div>
<div ng-switch-default>
<h2 ng-click="edit(post)">{{post.title}}</h2>
<p ng-click="edit(post)" bold-click ng-bind-html-unsafe="post.body"></p>
<p><em>posted on {{post.date|date:'short'}} by {{post.author}}</em></p>
</div>
<hr/>
<button ng-click="newPost()">New Post</button>
</ng-switch>
</script>

</body>
</html>

You'll see above that we've added two templates, posts and post, and put an ngView directive in their place. This is where the proper template will be inserted. Next, let's work on our config() call again:

blog.config(function ($httpProvider, $routeProvider) {
// .. a bunch of $httpProvider configuration

// you will be very sad if you do not start your URLs with "/"; this could be a bug
$routeProvider.when('/post/:id', {templateUrl: 'post'})
.when('/posts', {templateUrl: 'posts'})
.otherwise({redirectTo: '/posts'});
});
----

The above says when the URL ends with #/posts, load all posts. If the URL ends with #/post/1, load the post with ID 1.  You can also specify a controller (with the controller key in the route config object passed into when()) if you wish, but often times this is unnecessary if there is a controller already controlling your patrial.  

If we had a RESTful backend (which we don't), we could do this a little more easily with the $resource service. However, we'll have to make do by modifying the ContentCtrl controller to search through an array for the specified post if it gets one.

Modify the ContentCtrl controller to inject $routeParams, and put the following logic into the success() block:

blog.controller('ContentCtrl', function ($scope, Posts, Post, $routeParams) { 

.. clip .. 

// Retrieve the posts from our "server". If this succeeds, we'll
// put the posts into our $scope and do some post-processing.
Posts.getPosts().success(function (data) {
var posts = data.posts, i, post;
$scope.posts = [];
i = posts.length;
// if an ID is specified, search for it and set it to $scope.post
if ($routeParams.id) {
while (i--) {
if (posts[i].id === $routeParams.id) {
$scope.post = posts[i];
break;
}
}
} else {
// Create Post objects and put them into the list of posts.
i = posts.length;
while (i--) {
post = posts[i];
// convert to millisecond precision
$scope.posts.push(new Post(post.title, post.body, post.date * 1000,
post.author, true));
}
}
});
.. clip ..
});

As you can see that's kind of dumb, but that's the data we have. We could create another .json file to simulate a RESTful GET of one post, but that is pretty tedious. Regardless, if you can get your server to give you one post, by all means do that instead of this.

Unit Testing Routing

This likely makes more sense as a functional test. So for our unit test, let's mock out $routeParams instead:

describe('ContentCtrl', function () {
it('should put a single post in the scope if an ID appears in $routeParams', function () {
inject(function (Posts, Post, $httpBackend, $http) {
$http.defaults.transformRequest = []; // remove any fancy request transforms
// mock $routeParams; assume they got set by $route service
var $routeParams = {
id: 1
},
posts = [
{id: 1, title: 'foo'},
{id: 2, title: 'bar'}
];

$httpBackend.expectGET('data.json').respond({posts: posts});
$controller('ContentCtrl', {$scope: scope, Posts: Posts,
Post: Post, $routeParams: $routeParams});
$httpBackend.flush();
expect(scope.post).toBe(posts[0]);
expect(scope.posts).toEqual([]);
});
});
it('should put a list of posts in the scope if no ID appears in $routeParams', function() {
inject(function(Posts, Post, $httpBackend, $http) {
$http.defaults.transformRequest = []; // remove any fancy request transforms
// mock $routeParams; assume they got set by $route service
var $routeParams = {},
posts = [
{id: 1, title: 'foo'},
{id: 2, title: 'bar'}
];
$httpBackend.expectGET('data.json').respond({posts: posts});
$controller('ContentCtrl', {$scope: scope, Posts: Posts,
Post: Post, $routeParams: $routeParams});
$httpBackend.flush();
expect(scope.post).not.toBeDefined();
expect(scope.posts[0].title).toBe('bar');
expect(scope.posts[1].title).toBe('foo');
});
});
});

In the next chapter, we'll talk a bit more about using jQuery with AngularJS--when to use it, where to use it, and how to use it. These answers are "never", "nowhere" and "just don't", respectively. Unfortunately for everything but the simplest application written in AngularJS, you're probably going to want to use it somewhere. 

Another excellent utility AngularJS provides is promises. We'll discuss that next.

Promises, Promises

OK, what's a promise? When you have a promise, you know that one of two things will happen: the promise will be rejected or resolved. Think of this as the sad path and the happy path, respectively. It's important to understand that a promise is rejected/resolved once and only once. A promise may be rejected/resolved synchronously (such as with unit tests, or in cases where the async operation needn't be made), but typically it is done asynchronously. 

What do promises buy you? Aside from the guarantees they make, they avoid nasty code like this:

ajaxLib.GET('foo.url', function(response) {
ajaxLib.GET('bar.url', function(response) {
ajaxLib.GET('baz.url', function(response) {
// .. and so on
}
}
}

When you chain callbacks like this, it's called stair-stepping, and unless you want to go crazy, you should avoid this. Promises help you do that. You can do this:

ajaxLib.GET('foo.url')
.success(function(response) { return ajaxLib.GET('bar.url') })
.success(function(response) { return ajaxLib.GET('baz.url') });


 What's a deferred?

 It is an object responsible for giving you a promise and rejecting/resolving it.


 

The return value of each response will be passed to the next success function in the chain. Of course this is just hypothetical code; AngularJS and $q (the promise/deferred API) have their own syntax which I'll cover shortly.

If you have asynchronous code, and it executes a callback upon completion, it's a candidate to return a promise instead. The utility and power of promises blows a lot of continuation-passing style (CPS) code out of the water.

Promises in AngularJS

AngularJS features two major services out-of-the-box that use promises: $http and $timeout. Both return promises. A promise will have a then method, which accepts two parameters: a function accepting data if the promise is resolved, and a function accepting data if the promise is rejected. In the case of $http, the familiar success and error functions are available in addition to these. Instead of passing the entire response object, these break up the response into parameters. For example:

$http.get('foo.url').success(function(data, status, headers, config) { // do stuff })
.error(function(data, status, headers, config) { // do other stuff });

$http.get('foo.url').then(function(res) { // data, status, headers, config in res obj},
function(res) { // data, status, headers, config in res obj });

It should be noted that when attaching a promise to a scope object, there is a shortcut available.  The scope eats promises!  These two statements are equivalent:

$scope.foo = $http.get('foo.url');
$http.get('foo.url').success(function(res) {
$scope.foo = res;
});

This may save you some time, but you must understand that since get() is an asynchronous function, the value of "foo" in the scope will be immediately undefined, then set upon successful resolving of the call.

With $timeout, however, you are only afforded the then() function, which is fine and dandy. $timeout allows you to execute arbitrary code asynchronously, much like the setTimeout function in vanilla JavaScript. The difference is that it returns a promise instead of a timeout ID. Here is some equivalent code using setTimeout and $timeout:

var t = $window.setTimeout(function(successFn, errorFn) {
if (foo) {
successFn();
} else {
$window.clearTimeout(t);
errorFn();
}
}, 1000);

var p = $timeout(function() {
if (!foo) {
$timeout.cancel(p);
}
}, 1000).then(successFn, errorFn);

The promise is resolved when the timeout function executes successfully without being cancelled, and the successFn function is executed automatically. As you can see this actually saves you a bit of code.

By default, $timeout executes inside a digest loop. If you are not modifying the scope within the timeout function, you can inhibit this behavior to save some cycles. As a third parameter, pass false and the timeout function will not be executed inside an $apply block.

Chaining Promises

As in the first example, you can chain promises. The pattern looks like this:

$http.get('foo.url')
.then(function (res) {
// do stuff with res
if (something) {
return $q.reject(first_sad_obj);
}
return first_happy_obj;
}, function (res) {
// do other stuff with res
return first_sad_obj;
})
.then(function (first_happy_obj) {
// do stuff
if (something_else) {
return $q.reject(second_sad_obj);
}
return second_happy_obj;
}, function (first_sad_obj) {
// more stuff to do
return second_sad_obj;
})
.then(function (second_happy_obj) {
// success
}, function (second_sad_obj) {
// failure. etc.
});

If we return an object from a promise, it will be passed to the next promise in the chain. If we use $q.reject and return that instead, the promise will be rejected and the next promise in the chain will have its second (failure) function executed. And so on and so forth.

I've used promises to wait until a modal dialog's template was fetched and the dialog was opened to execute some code (if the template was already fetched, the promise is resolved immediately, synchronously; this is perfectly acceptable). I've used promises to wait for controller x to finish asynchronously processing some data so controller y could start its processing of the data.

You can even combine promises!

var p1 = asyncCode1();
var p2 = asyncCode2();
var p3 = $q.all(p1, p2);
// then, asyncCode1 resolves; p1 is resolved, but p3 is not
// next, asyncCode2 resolves; p1 is resolved and p2 is resolved, now p3 becomes resolved

Using the all function, you can resolve one promise when another set of promises are all resolved. This can come in handy if you are waiting on two different things to asynchronously happen.  

It's easy to propagate errors using promises.  Here's an example of doing it the old-fashioned, callback-chain way:

ajaxLib.get('url').then(function() {, {success: function() {
ajaxLib.get('anotherUrl', {success: function() {
ajaxLib.get('yetAnotherUrl', {success: function() {
}, error: function() { // pop error });
}, error: function() { // pop error });
}, error: function() { // pop error });

This is unwieldy.  Promises offer a nice alternative, since they know whether or not they have been resolved or rejected:

ajaxLib.get('url').then(function(res) {
}).then(function(res) {
return ajaxLib.get('anotherUrl'); 
}).then(function(res) {
 return ajaxLib.get('yetAnotherUrl');
}, function(error) {
// pop error
});

In the above example, the first error along the way will trickle down to the final "rejected" function.  

Note that sometimes promises can take the place of a $watch, or vice-versa. Here's an example:

// note no module here; not necessary for example.
function ParentCtrl($q, $http) {
$scope.getFoo = function() {
return $http.get('foo.url').success(function(data) {
$scope.foo = data;
});
};
}
function ChildCtrl() {
$scope.getFoo().then(function() {
// do something with $scope.foo
};
}

And the alternative:

function ParentCtrl($http) {
$scope.getFoo = function() {
$http.get('foo.url').success(function(data) {
$scope.foo = data;
});
};
}

function ChildCtrl() {
var foo_watch = $scope.$watch('foo', function(foo, old_foo) {
if (foo !== old_foo && !!foo) {
// do something with foo
foo_watch(); // stop watching
}
});
$scope.getFoo();
}

Which is better? I'd call the promise strategy more elegant. I'd say use the $watch strategy only if you need to execute some code against foo more than once. Don't cancel the $watch by calling its return function, and you got it. Otherwise, if you did this with promises, you'd have to set up a $watch anyways!

Debugging AngularJS Apps

While, on the main, AngularJS is easy to debug, there is one issue that complicates things. If a directive is not recognized for whatever reason, it silently fails. That means your directive code can have a syntax error or something, but it'll never get run because the directive won't register and AngularJS just skips any attribute it doesn't understand. When writing directives then, it's often helpful to have a simple console.log (or $log.log as you'll see below) in there to assert it even got linked/compiled. If you don't see the log message, AngularJS didn't recognize your directive.

Firebug & Chrome Developer Tools

These two tools are indispensible. I don't know how you can write a client-side JavaScript application without them. If you use Firefox you can get Firebug at http://getfirebug.com. If you use IE, or at least have to put up with IE, you can also get Firebug Lite. Chrome Dev Tools come preinstalled. Both do their jobs well enough, but if you use Chrome you have the added bonus of access to the AngularJS Batarang!

The AngularJS Batarang!

If you use Chrome (and you should really try it if you haven't; the developer tools are quite good), you can install a Chrome extension (or whatever they call it) called the AngularJS Batarang (https://chrome.google.com/webstore/detail/angularjs-batarang). This is a project by Brian Ford (http://briantford.com), an AngularJS whiz. With this amazing tool, you can inspect any element on the page and easily view its scope within the right-hand panes (where the CSS style information lives). This saves you from having to console.log($scope) all over the place to see what's in there (note you cannot just dump the scope using {{}}; even if you can get to the scope object, it just outputs as "SCOPE", which is not helpful).

In addition to that, you can examine the entire scope hierarchy at a glance within the Batarang's own tab. If you have a lot of scopes, this display can seem overwhelming, but it's a great way to visualize your app as a whole. There are other tools within the Batarang, but these two are the ones I find most useful.

$log

AngularJS provides $log, which an alias for window.console by default, unless you redefine it. So console.log becomes $log.log. Might save you some keystrokes, but you still have to inject the thing. The real power of $log is that you CAN redefine it (or any other service or directive in AngularJS for that matter) and make it do whatever you want. Want it to pop alerts instead for some bizarre reason? You can do that. You can output errors to the HTML if you want. $log is a provider, so if you are going to redefine it, be sure to use Module.provider (an example of this is below).

$exceptionHandler

By default, AngularJS traps all exceptions coming out of it. That is important information to know if you are wondering why window.onerror never gets called. What you see when you see AngularJS exceptions (or exceptions in your AngularJS code) is simply a call to $log.error or something akin to that. Redefine the $exceptionHandler class to rethrow errors if you wish to attach behavior to window.onerror, or redefine it to do whatever you want. Here's an example that we can use in our blog:

blog.provider('$exceptionHandler', function () {
this.$get = function() {
return function(e) {
throw e;
};
};
});

That's it!

I18n and L10n

AngularJS provides some utility for localizing your apps. As of this writing it supports localization for the datetime, number and currency filters. Of course, I'm not sure how much sense the currency filter makes when you have a currency like 5.00 and you can choose to display it as $5.00 or €5.00. That'd be great if the exchange rates were 1:1!

In addition to this, one of the directives, ngPluralize (which helps you do stuff like display "1 sock" or "2 socks" depending on certain criteria) is localizable.

The $locale service will provide you with the current locale ID of the browser using $locale.id.

Conclusion

You should now have some ideas about practical applications of AngularJS. In the next, and final, chapter, we will look at how Angular plays with third party technologies.

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.