Angular.js is an awesome framework for medium-complexity, single-page web applications. Its feature set and use cases fit perfectly in the complexity gap between Backbone.js and Ember.js.
Composed of many interesting and useful features such as animation and touch interaction, Angular provides an architecture that makes your single-page web app a joy to work with.
1. Promises, Promises, Promises
Promises are nice, and they help to avoid a common problem in JavaScript: callback hell. Angular provides the $q service, which is inspired by Kris Kowal’s Q. It turns a common Backbone.js site:
doThing1("a", function(thing1) {
doThing2("b", function(thing2) {
doThing3("c", function(thing3) {
console.log("I am the triangle of doom", thing3)
});
});
});
into elegant chains of functions in Angular:
doThing1().
then(function(thing1) {...}).
then(function(thing2){...}).
then(function(thing3) {...}).
catch(function(error) {...}).
final(function(){...})
Promises are spread throughout the framework, most importantly in network access and the router.
2. HTTP and REST Made Easy
Angular, similar to Ember, is built up on layers of abstractions. The $http
service makes hitting a URL easy, and also utilizes promises. If we wanted to do a POST to the /contact URL using $http
, it would look like this:
$http({method: 'POST', url: '/contact', params: {name: "Joe", email: "[email protected]"}).
success(function(data, status, headers, config) {
// do something with our response
}).
error(function(data, status, headers, config) {
// process the error response
});
Building up on top of $http is $resource, which makes RESTful API interactions a lot thinner than if they were implemented with $http. Retrieving a contact by id would look as follows:
var Contact = $resource('/contact/:id', {id:'@id'});
var joe = Contact.get({id:42}).$promise.
then(function(data) {
// process contact #42
}).
catch(function(response) {
// process the error response
})
Building up a further layer of abstraction is Restangular. While not maintained by the Angular core community, Restangular is well-documented and makes RESTful interactions even simpler than $resource. Transforming our contact example to utilize Restangular would look as follows:
Restangular.one('contact', 42).
then(function(data){...}).
catch(function(response){...})
Restangular provides caching via ETags for free, easy nested resources and the ability to quickly generate a URL or specify your own. Much like everything else so far, Restangular always returns a promise. This makes resolved Restangular resources easy to inject into controllers using Angular router’s resolve.
$routeProvider.when('/contacts/:id', {
templateUrl: template_path("contact_details.html"),
controller: "ContactCtrl",
resolve: {
contact: ["$route", "Restangular", function($route, Restangular) {
// contact resource with :id is injected into ContactCtrl when resolved
return Restangular.one('contacts', $route.current.params.id).get();
})]
});
3. Dependency Injection – Angular’s Foundation
Angular’s dependency injection capabilities are by far my favorite functionality built into Angular. The entirety of the framework, as demonstrated above with the $routeProvider’s resolve capabilities, is structured around injection. Angular’s dependency injection has also inspired other projects, such as intravenous in the node ecosystem.
Dependency injection makes the Angular framework and single page web apps built on it extremely modular and testable. Dependency injection is structure into 2 components, the objects being injected, and the objects which have their dependencies injected into them.
The objects being injected are known as values, constants, factories, services and providers, ordered from least complex to most. I won’t go into too much detail about the differences since each one is well documented by the Angular team. The following code defines an Authentication factory which can be injected into other objects.
myModule.factory("Authentication", function() {
return {
login: function(user, password) {...}
};
});
Lets modify our Authentication factory to have Restangular injected into it.
myModule.factory("Authentication", function(Restangular) {
return {
login: function(user, password) {
return Restangular.one("/login").post({user: user, password: password});
}
};
});
The complication here is that since angular’s dependency resolution is based off of the name of our argument, minification will cause our dependency name to be lost. This can be resolved in a variety of ways but the most favored, due to it’s ability to be inlined, is as follows:
myModule.factory("Authentication", ["Restangular", function(Restangular) {
return {
login: function(user, password) {
return Restangular.one("/login").post({user: user, password: password});
}
};
}]);
There are many more reasons than just the 3 above to give Angular a try. It makes a great alternative to Backbone.js!
What is your favorite feature of Angular?