Angular Zen

Exclusive offer: get 50% off this eBook here
Mastering Web Application Development with AngularJS

Mastering Web Application Development with AngularJS — Save 50%

Build single-page web applications using the power of AngularJS with this book and ebook

$26.99    $13.50
by Pawel Kozlowski Peter Bacon Darwin | September 2013 | Open Source Web Development

This article written by Pawel Kozlowski and Peter Bacon Darwin, the authors of Mastering Web Application Development with AngularJS, serves as an introduction to AngularJS, both the framework and the project behind it. Firstly we are going to take a brief look at the project itself: who drives it, where to find the source code and the documentation, how to ask for help, and so on.

Most of this article is filled with introduction to the AngularJS framework, its core concepts, and coding patterns. There is a lot of material to cover, so to make the learning process fast and painless, there are plenty of code examples.

AngularJS is a unique framework that without doubt will shape the web development space in the years to come. This is why the last part of this article explains what makes AngularJS so special, how it compares to other existing frameworks, and what we can expect from it in the future.

In this article we will cover the following topics:

  • How to write a simple Hello World application in AngularJS. In the process of doing so, you will come to know where to find framework source code, its documentation, and community.
  • To get familiar with the basic building blocks of any AngularJS application: templates with directives, scopes, and controllers.
  • To become aware of the AngularJS sophisticated dependency injection system with all its nuances.
  • To understand how AngularJS compares to other frameworks and libraries (especially jQuery) and what makes it so special.

(For more resources related to this topic, see here.)

Meet AngularJS

AngularJS is a client-side MVC framework written in JavaScript. It runs in a web browser and greatly helps us (developers) to write modern, single-page, AJAX-style web applications. It is a general purpose framework, but it shines when used to write CRUD (Create Read Update Delete) type web applications.

Getting familiar with the framework

AngularJS is a recent addition to the client-side MVC frameworks list, yet it has managed to attract a lot of attention, mostly due to its innovative templating system, ease of development, and very solid engineering practices. Indeed, its templating system is unique in many respects:

  • It uses HTML as the templating language
  • It doesn't require an explicit DOM refresh, as AngularJS is capable of tracking user actions, browser events, and model changes to figure out when and which templates to refresh
  • It has a very interesting and extensible components subsystem, and it is possible to teach a browser how to interpret new HTML tags and attributes

The templating subsystem might be the most visible part of AngularJS, but don't be mistaken that AngularJS is a complete framework packed with several utilities and services typically needed in single-page web applications.

AngularJS also has some hidden treasures, dependency injection (DI) and strong focus on testability. The built-in support for DI makes it easy to assemble a web application from smaller, thoroughly tested services. The design of the framework and the tooling around it promote testing practices at each stage of the development process.

Finding your way in the project

AngularJS is a relatively new actor on the client-side MVC frameworks scene; its 1.0 version was released only in June 2012. In reality, the work on this framework started in 2009 as a personal project of Miško Hevery, a Google employee. The initial idea turned out to be so good that, at the time of writing, the project was officially backed by Google Inc., and there is a whole team at Google working full-time on the framework.

AngularJS is an open source project hosted on GitHub (https://github.com/angular/angular.js) and licensed by Google, Inc. under the terms of the MIT license.

The community

At the end of the day, no project would survive without people standing behind it. Fortunately, AngularJS has a great, supportive community. The following are some of the communication channels where one can discuss design issues and request help:

AngularJS teams stay in touch with the community by maintaining a blog (http://blog.angularjs.org/) and being present in the social media, Google + (+ AngularJS), and Twitter (@angularjs). There are also community meet ups being organized around the world; if one happens to be hosted near a place you live, it is definitely worth attending!

Online learning resources

AngularJS has its own dedicated website (http://www.angularjs.org) where we can find everything that one would expect from a respectable framework: conceptual overview, tutorials, developer's guide, API reference, and so on. Source code for all released AngularJS versions can be downloaded from http://code.angularjs.org.

People looking for code examples won't be disappointed, as AngularJS documentation itself has plenty of code snippets. On top of this, we can browse a gallery of applications built with AngularJS (http://builtwith.angularjs.org). A dedicated YouTube channel (http://www.youtube.com/user/angularjs) has recordings from many past events as well as some very useful video tutorials.

Libraries and extensions

While AngularJS core is packed with functionality, the active community keeps adding new extensions almost every day. Many of those are listed on a dedicated website: http://ngmodules.org.

Tools

AngularJS is built on top of HTML and JavaScript, two technologies that we've been using in web development for years. Thanks to this, we can continue using our favorite editors and IDEs, browser extensions, and so on without any issues. Additionally, the AngularJS community has contributed several interesting additions to the existing HTML/JavaScript toolbox.

Batarang

Batarang is a Chrome developer tool extension for inspecting the AngularJS web applications. Batarang is very handy for visualizing and examining the runtime characteristics of AngularJS applications. We are going to use it extensively in this article to peek under the hood of a running application. Batarang can be installed from the Chrome's Web Store (AngularJS Batarang) as any other Chrome extension.

Plunker and jsFiddle

Both Plunker (http://plnkr.co) and jsFiddle (http://jsfiddle.net) make it very easy to share live-code snippets (JavaScript, CSS, and HTML). While those tools are not strictly reserved for usage with AngularJS, they were quickly adopted by the AngularJS community to share the small-code examples, scenarios to reproduce bugs, and so on. Plunker deserves special mentioning as it was written in AngularJS, and is a very popular tool in the community.

IDE extensions and plugins

Each one of us has a favorite IDE or an editor. The good news is that there are existing plugins/extensions for several popular IDEs such as Sublime Text 2 (https://github.com/angular-ui/AngularJS-sublime-package), Jet Brains' products (http://plugins.jetbrains.com/plugin?pr=idea&pluginId=6971), and so on.

Mastering Web Application Development with AngularJS Build single-page web applications using the power of AngularJS with this book and ebook
Published: August 2013
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

AngularJS crash course

Now that we know where to find the library sources and their accompanying documentation, we can start writing code to actually see AngularJS in action.

Hello World – the AngularJS example

Let's have a look at the typical "Hello, World!" example written in AngularJS to get the first impression of the framework and the syntax it employs.

<html> <head> <script src = "http://ajax.googleapis.com/ajax/libs/angularjs/
1.0.7/angular.js"
>

</script> </head> <body ng-app ng-init="name = 'World'"> <h1>Hello, {{name}}!</h1> </body> </html>

First of all, we need to include the AngularJS library to make our sample run correctly in a web browser. It is very easy as AngularJS, in its simplest form, is packaged as a single JavaScript file.

AngularJS library is a relatively small one: a minified and gzipped version has a size of around 30 KB. A minified version without gzip compression has a size of around 80 KB. It doesn't require any third-party dependencies.

For the short examples in this article we are going to use an un-minified, developer-friendly version hosted on Google's content delivery network (CDN). Source code for all versions of AngularJS can be also downloaded from http://code.angularjs.org.

Including the AngularJS library is not enough to have a running example. We need to bootstrap our mini application. The easiest way of doing so is by using the custom ng-app HTML attribute.

Closer inspection of the <body> tag reveals another non-standard HTML attribute: ng-init. We can use ng-init to initialize model before a template gets rendered. The last bit to cover is the {{name}} expression which simply renders model value.

Even this very first, simple example brings to light some important characteristics of the AngularJS templating system, which are as follows:

  • Custom HTML tags and attributes are used to add dynamic behavior to an otherwise static HTML document
  • Double curly braces ({{expression}}) are used as a delimiter for expressions outputting model values

In the AngularJS, all the special HTML tags and attributes that the framework can understand and interpret are referred to as directives.

Two-way data binding

Rendering a template is straightforward with AngularJS; the framework shines when used to build dynamic web applications. In order to appreciate the real power of AngularJS, let's extend our "Hello World" example with an input field, as shown in the following code:

<body ng-app ng-init="name = 'World'"> Say hello to: <input type="text" ng-model="name"> <h1>Hello, {{name}}!</h1> </body>

There is almost nothing special about the <input> HTML tag apart from the additional ng-model attribute. The real magic happens as soon as we begin to type text into the <input> field. All of a sudden the screen gets repainted after each keystroke, to reflect the provided name! There is no need to write any code that would refresh a template, and we are not obliged to use any framework API calls to update the model. AngularJS is smart enough to detect model changes and update the DOM accordingly.

Most of the traditional templating system renders templates in a linear, one-way process: a model (variables) and a template are combined together to produce a resulting markup. Any change to the model requires re-evaluation of a template. AngularJS is different because any view changes triggered by a user are immediately reflected in the model, and any changes in the model are instantly propagated to a template.

The MVC pattern in AngularJS

Most existing web applications are based on some form of the well-known model-view-controller (MVC) pattern. But the problem with the MVC is that it is not a very precise pattern, but rather a high-level, architectural one. Worse yet, there are many existing variations and derivatives of the original pattern (MVP and MVVM seem to be the most popular ones). To add to the confusion, different frameworks and developers tend to interpret the mentioned patterns differently. This results in situations where the same MVC name is used to describe different architectures and coding approaches. Martin Fowler summarizes this nicely in his excellent article on GUI architectures (http://martinfowler.com/eaaDev/uiArchs.html):

Take Model-View-Controller as an example. It's often referred to as a pattern, but I don't find it terribly useful to think of it as a pattern because it contains quite a few different ideas. Different people reading about MVC in different places take different ideas from it and describe these as 'MVC'. If this doesn't cause enough confusion you then get the effect of misunderstandings of MVC that develop through a system of Chinese whispers.

The AngularJS team takes a very pragmatic approach to the whole family of MVC patterns, and declares that the framework is based on the MVW (model-view-whatever) pattern. Basically one needs to see it in action to get the feeling of it.

Bird's eye view

All the "Hello World" examples we've seen so far didn't employ any explicit layering strategy: data initialization, logic, and view were all mixed together in one file. In any real-world application, though, we need to pay more attention to a set of responsibilities assigned to each layer. Fortunately, AngularJS provides different architectural constructs that allows us to properly build more complex applications.

All the subsequent examples throughout the article omit the AngularJS initialization code (scripts inclusion, ng-app attribute, and so on) for readability.

Let's have a look at the slightly modified "Hello World" example:

<div ng-controller="HelloCtrl"> Say hello to: <input type="text" ng-model="name"><br> <h1>Hello, {{name}}!</h1> </div>

The ng-init attribute was removed, and instead we can see a new ng-controller directive with a corresponding JavaScript function. The HelloCtrl accepts a rather mysterious $scope argument as follows:

var HelloCtrl = function ($scope) { $scope.name = 'World'; }

Scope

A $scope object in AngularJS is here to expose the domain model to a view (template). By assigning properties to scope instances, we can make new values available to a template for rendering.

Scopes can be augmented with both data and functionality specific to a given view. We can expose UI-specific logic to templates by defining functions on a scope instance.

For example, one could create a getter function for the name variable, as given in the following code:

var HelloCtrl = function ($scope) { $scope.getName = function() { return $scope.name; }; }

And then use it in a template as given in the following code:

<h1>Hello, {{getName()}}!</h1>

The $scope object allows us to control precisely which part of the domain model and which operations are available to the view layer. Conceptually, AngularJS scopes are very close to the ViewModel from the MVVM pattern.

Controller

The primary responsibility of a controller is to initialize scope objects. In practice, the initialization logic consists of the following responsibilities:

  • Providing initial model values
  • Augmenting $scope with UI-specific behavior (functions)

Controllers are regular JavaScript functions. They don't have to extend any framework-specific classes nor call any particular AngularJS APIs to correctly perform their job.

Please note that a controller does the same job as the ng-init directive, when it comes to setting up initial model values. Controllers make it possible to express this initialization logic in JavaScript, without cluttering HTML templates with code.

Model

AngularJS models are plain, old JavaScript objects. We are not obliged to extend any of the framework's base classes nor construct model objects in any special way.

It is possible to take any existing, pure JavaScript classes or objects and use them in the same way as in the model layer. We are not limited to model properties being represented by primitive values (any valid JavaScript object or an array can be used). To expose a model to AngularJS you simply assign it to a $scope.

AngularJS is not intrusive and lets us keep model objects free from any framework-specific code.

Scopes in depth

Each $scope is an instance of the Scope class. The Scope class has methods that control the scope's lifecycle, provide event-propagation facility, and support the template rendering process.

Hierarchy of scopes

Let's have another look at the simple HelloCtrl example, which we've examined already:

var HelloCtrl = function ($scope) { $scope.name = 'World'; }

The HelloCtrl looks similar to a regular JavaScript constructor function, there is absolutely nothing special about it apart from the $scope argument. Where might this argument might be coming from?

A new scope was created by the ng-controller directive using the Scope.$new() method call. Wait a moment; it looks like we need to have at least one instance of a scope to create a new scope! Indeed, AngularJS has a notation of the $rootScope (a scope that is a parent of all the other scopes). The $rootScope instance gets created when a new application is bootstrapped.

The ng-controller directive is an example of a scope-creating directive. AngularJS will create a new instance of the Scope class whenever it encounters a scope-creating directive in the DOM tree. A newly-created scope will point to its parent scope using the $parent property. There can be many scope-creating directives in the DOM tree and as a result many scopes will be created.

Scopes form a parent-child, tree-like relationship rooted at the $rootScope instance. As scopes' creation is driven by the DOM tree, it is not surprising that scopes' tree will mimic the DOM structure.

Now that we know that some directives create new child scopes you might be wondering why all this complexity is needed. To understand this, let's have a look at the example that makes use of a ng-repeat repeater directive.

The controller is as follows:

var WorldCtrl = function ($scope) { $scope.population = 7000; $scope.countries = [ {name: 'France', population: 63.1}, {name: 'United Kingdom', population: 61.8}, ]; };

And the markup fragment looks in the following manner:

<ul ng-controller="WorldCtrl"> <li ng-repeat="country in countries"> {{ country.name }} has population of {{ country.population }} </li> <hr> World's population: {{ population }} millions </ul>

The ng-repeat directive allows us to iterate over a collection of countries and create new DOM elements for each item in a collection. The syntax of the ng-repeat directive should be easy to follow; a new variable country is created for each item and exposed on a $scope to be rendered by a view.

But there is a problem here, that is, a new variable needs to be exposed on a $scope for each country and we can't simply override previously exposed values. AngularJS solves this problem by creating a new scope for each element in a collection. Newly created scopes will form a hierarchy closely matching the DOM tree structure, and we can visualize this by using the excellent Batarang extension for Chrome as shown in the following screenshot:

As we can see in the screenshot, each scope (boundaries marked with a rectangle) holds its own set of model values. It's possible to define the same variable on different scopes without creating name collisions (different DOM elements will simply point to different scopes and use variables from a corresponding scope to render a template). This way each item has its own namespace, in the previous example every <li> element gets its own scope where the country variable can be defined.

Scopes hierarchy and inheritance

Properties defined on one scope are visible to all child scopes, provided that a child scope doesn't redefine a property using the same name! This is very useful in practice, since we don't need to redefine over and over again properties that should be available throughout a scope hierarchy.

Building on our previous example, let's assume that we want to display the percentage of the world's population that lives in a given country. To do so, we can define the worldsPercentage function on a scope managed by the WorldCtrl as given in the following code:

$scope.worldsPercentage = function (countryPopulation) { return (countryPopulation / $scope.population)*100 ; }

And then call this function from each scope instance created by the ng-repeat directive as follows:

<li ng-repeat="country in countries"> {{country.name}} has population of {{country.population}}, {{ worldsPercentage(country.population )}} % of the World's population </li>

Scope's inheritance in AngularJS follows the same rules as prototypical inheritance in JavaScript (when we try to read a property, the inheritance tree will be traversed upwards till a property is found).

Perils of the inheritance through the scopes hierarchy

Inheritance through the scopes hierarchy is rather intuitive and easy to understand when it comes to the read access. When it comes to the write access, however, things become a little bit complicated.

Let's see what happens if we define a variable on one scope and omit if from a child scope. The JavaScript code is as follows:

var HelloCtrl = function ($scope) { };

And the view code is as follows:

<body ng-app ng-init="name='World'"> <h1>Hello, {{name}}</h1> <div ng-controller="HelloCtrl"> Say hello to: <input type="text" ng-model="name"> <h2>Hello, {{name}}!</h2> </div> </body>

If you try to run this code, you will observe that the name variable is visible across the whole application; even if it was defined on the top-most scope only! This illustrates that variables are inherited down the scope hierarchy. In other words, variables defined on a parent scope are accessible in child scopes.

Now, let's observe what will happen if we start to type text into the <input> box, as shown in the following screenshot:

You might be a bit surprised to see that a new variable was created in the scope initialized by the HelloCtrl controller, instead of changing a value set up on the $rootScope instance. This behavior becomes less surprising when we realize that scopes prototypically inherit from each other. All the rules that apply to the prototypical inheritance of objects in JavaScript apply equally to scopes prototypical inheritance. Scopes are just JavaScript objects after all.

There are several ways of influencing properties defined on a parent scope from a child scope. Firstly, we could explicitly reference a parent scope using the $parent property. A modified template would look as follows:

<input type="text" ng-model="$parent.name">

While it is possible to solve an issue using this example by directly referencing a parent scope, we need to realize that this is a very fragile solution. The trouble is that an expression used by the ng-model directive makes strong assumptions about the overall DOM structure. It is enough to insert another scope-creating directive somewhere above the <input> tag and $parent will be pointing to a completely different scope.

As a rule of thumb, try to avoid using the $parent property as it strongly links AngularJS expressions to the DOM structure created by your templates. An application might easily break as a result of simple changes in its HTML structure.

Another solution involves binding to a property of an object and not directly to a scope's property. The code for this solution is as follows:

<body ng-app ng-init="thing = {name : 'World'}">
<h1>Hello, {{thing.name}}</h1>
<div ng-controller="HelloCtrl">
Say hello to: <input type="text" ng-model="thing.name">
<h2>Hello, {{thing.name}}!</h2>
</div>
</body>

This approach is much better as it doesn't assume anything about the DOM tree structure.

Avoid direct bindings to scope's properties. Two-way data binding to object's properties (exposed on a scope) is a preferred approach.

As a rule of thumb, you should have a dot in an expression provided to the ng-model directive (for example, ng-model="thing.name").

AngularJS core services and directives make use of this event bus to signal important changes in the application's state. For example, we can listen to the $locationChangeSuccess event (broadcasted from the $rootScope instance) to be notified whenever a location (URL in a browser) changes, as given in the following code:

$scope.$on('$locationChangeSuccess', function(event, newUrl, oldUrl){
//react on the location change here
//for example, update breadcrumbs based on the newUrl
});

The $on method available on each scope instance can be invoked to register a scope-event handler. A function acting as a handler will be invoked with a dispatched event object as its first argument. Subsequent arguments will correspond to the event's payload and are event-type dependent.

Similar to the DOM events, we can call the preventDefault() and stopPropagation() methods on event object. The stopPropagation() method call will prevent an event from bubbling up the scopes' hierarchy, and is available only for events dispatched upwards in the hierarchy ($emit).

While AngularJS event system is modeled after the DOM one, both event propagation systems are totally independent and have got no common parts.

While events propagated through the scopes' hierarchy are very elegant solutions to several problems (especially when it comes to notifications related to global, asynchronous state changes), those should be used sparingly. Usually we can rely on the two-way data binding to end up with a cleaner solution. In the entire AngularJS framework, there are only three events being emitted ($includeContentRequested, $includeContentLoaded, $viewContentLoaded), and seven events being broadcasted ($locationChangeStart, $locationChangeSuccess, $routeUpdate, $routeChangeStart, $routeChangeSuccess, $routeChangeError, $destroy). As you can see, scope events are used very sparingly and we should evaluate other options (mostly the two-way data binding) before sending custom events.

Don't try to mimic the DOM event-based programming model in AngularJS. Most of the time there are better ways of structuring your application, and you can go very far with the two-way data binding.

Scopes lifecycle

Scopes are necessary to provide isolated namespaces and avoid variable name collisions. Scopes which are smaller and organized in a hierarchy help in managing memory usage. When one of the scopes is no longer needed, it can be destroyed. As a result, model and functionality exposed on this scope will be eligible for garbage collection.

New scopes are usually brought to life and destroyed by the scope-creating directives. It is also possible to manually create and destroy scopes by calling the $new() and $destroy() methods, respectively (both methods are defined on the Scope type).

View

We've seen enough examples of AngularJS templates to realize that it is not yet another templating language, but quite a different beast. Not only does the framework rely on the HTML for its template syntax and allow us to extend the HTML vocabulary, but it has the unique ability to refresh parts of the screen without any manual intervention!

In reality, AngularJS has even more intimate connections to HTML and the DOM as it depends on a browser to parse the template's text (as a browser would do with any other HTML document). After a browser is done transforming the markup's text to the DOM tree, AngularJS kicks in and traverses the parsed DOM structure. Each time it encounters a directive, AngularJS executes its logic to turn directives into dynamic parts of the screen.

Since AngularJS depends on a browser to parse templates, we need to ensure that the markup represents valid HTML. Pay special attention to close the HTML tags properly (failing to do so won't produce any error messages, but the view won't be rendered correctly). AngularJS works using the live, valid DOM tree!

AngularJS makes it possible to enrich HTML's vocabulary (we can add new attributes or HTML elements and teach a browser how to interpret them). It is almost similar to creating a new domain-specific language (DSL) on top of HTML and instructing a browser on how to make sense of the new instructions. You can often hear that AngularJS "teaches browsers new tricks".

Declarative template view – the imperative controller logic

What is probably more important, however, is not the syntax and functionality of individual directives but rather the underlying AngularJS philosophy of building UIs.

AngularJS promotes a declarative approach to UI construction. What it means in practice is that templates are focused on describing a desired effect rather than on ways of achieving it. It all might sound a bit confusing, so an example might come in handy here.

Let's imagine that we were asked to create a form where a user can type in a short message, and then send it by clicking on a button. There are some additional user-experience (UX) requirements such as message size should be limited to 100 characters, and the Send button should be disabled if this limit is exceeded. A user should know how many characters are left as they type. If the number of remaining characters is less than ten, the displayed number should change the display style to warn users. It should be possible to clear text of a provided message as well. A finished form looks similar to the following screenshot:

The preceding requirements are not particularly challenging and describe a fairly standard text form. Nevertheless, there are many UI elements to coordinate here such as we need to make sure that the button's disabled state is managed correctly, the number of remaining characters is accurate and displayed with an appropriate style, and so on. The very first implementation attempt looks as follows:

<div class="container" ng-controller="TextAreaWithLimitCtrl"> <div class="row"> <textarea ng-model="message">{{message}}</textarea> </div> <div class="row"> <button ng-click="send()">Send</button> <button ng-click="clear()">Clear</button> </div> </div>

Let's use the preceding code as a starting point and build on top of it. Firstly, we need to display the number of remaining characters, which is easy enough, as given in the following code:

<span>Remaining: {{remaining()}}</span>

The remaining() function is defined in the TextAreaWithLimitCtrl controller on the $scope as follows:

$scope.remaining = function () {
return MAX_LEN - $scope.message.length;
};

Next, we need to disable the Send button if a message doesn't comply with the required length constraints. This can be easily done with a little help from the ng-disabled directive as follows:

<button ng-disabled="!hasValidLength()"...>Send</button>

We can see a recurring pattern here. To manipulate UI, we only need to touch a small part of a template and describe a desired outcome (display number of remaining characters, disable a button, and so on) in terms of the model's state (size of a message in this case). The interesting part here is that we don't need to keep any references to DOM elements in the JavaScript code and we are not obliged to manipulate DOM elements explicitly. Instead we can simply focus on model mutations and let AngularJS do the heavy lifting. All we need to do is to provide some hints in the form of directives.

Coming back to our example, we still need to make sure that the number of remaining characters changes style when there are only a few characters left. This is a good occasion to see one more example of the declarative UI in action, as given in the following code:

<span ng-class="{'text-warning' : shouldWarn()}"> Remaining: {{remaining()}}</span>

where the shouldWarn() method is implemented as follows:

$scope.shouldWarn = function () {
return $scope.remaining() < WARN_THRESHOLD;
};

The CSS class change is driven by the model mutation, but there is no explicit DOM manipulation logic anywhere in the JavaScript code! UI gets repainted based on a declaratively expressed "wish". What we are saying using the ng-class directive is this: "the text-warning CSS class should be added to the <span> element, every time a user should be warned about exceeded character limit". This is different from saying that "when a new character is entered and the number of characters exceeds the limit, I want to find a <span> element and change the text-warning CSS class of this element".

What we are discussing here might sound like a subtle difference, but in fact declarative and imperative approaches are quite opposite. The imperative style of programming focuses on describing individual steps leading to a desired outcome. With the declarative approach, focus is shifted to a desired result. The individual little steps taken to reach to this result are taken care of by a supporting framework. It is like saying "Dear AngularJS, here is how I want my UI to look when the model ends up in a certain state. Now please go and figure out when and how to repaint the UI".

The declarative style of programming is usually more expressive as it frees developers from giving very precise, low-level instructions. The resulting code is often very concise and easy to read. But for the declarative approach to work, there must be machinery that can correctly interpret higher-level orders. Our programs start to depend on these machinery decisions and we need to give up some of the low-level control. With the imperative approach, we are in full control and can fine tune each and every single operation. We've got more control, but the price to pay for "being in charge" is a lot of lower-level, repetitive code to be written.

People familiar with SQL language will find all this sounding familiar (SQL is a very expressive, declarative language for adhoc data querying). We can simply describe the desired result (data to be fetched) and let a (relational) database figure out how to go about retrieving specified data. Most of the time, this process works flawlessly and we quickly get what we have asked for. Still there are cases where it is necessary to provide additional hints (indexes, query planner hints, and so on) or take control over data-retrieval process to fine tune performance.

Directives in AngularJS templates declaratively express the desired effect, so we are freed from providing step-by-step instructions on how to change individual properties of DOM elements (as is often the case in applications based on jQuery). AngularJS heavily promotes declarative style of programming for templates and imperative one for the JavaScript code (controllers and business logic). With AngularJS , we rarely apply low-level, imperative instructions to the DOM manipulation (the only exception being code in directives).

As a rule of thumb, one should never manipulate the DOM elements in AngularJS controllers. Getting a reference to a DOM element in a controller and manipulating element's properties indicates imperative approach to UI - something that goes against AngularJS way of building UIs.

Declarative UI templates written using AngularJS directives allow us to describe quickly complex, interactive UIs. AngularJS will take all the low-level decisions on when and how to manipulate parts of the DOM tree. Most of the time AngularJS does "the right thing" and updates the UI as expected (and in a timely fashion). Still, it is important to understand the inner workings of AngularJS, so that we can provide appropriate hints to the framework if needed. Using the SQL analogy once again here, most of the time we don't need to worry about the work done by a query planner. But when we start to hit performance problems, it is good to know how query planner arrived at its decisions so that we can provide additional hints. The same applies to UIs managed by AngularJS: we need to understand the underlying machinery to effectively use templates and directives.

Modules and dependency injection

Vigilant readers have probably noticed that all the examples presented so far were using global constructor functions to define controllers. But global state is evil, it hurts application structure, makes code hard to maintain, test, and read. By no means is AngularJS suggesting usage of global state. On the contrary, it comes with a set of APIs that make it very easy to define modules and register objects in those modules.

Modules in AngularJS

Let's see how to turn an ugly, globally-defined controller definition into its modular equivalent, before a controller is declared as follows:

var HelloCtrl = function ($scope) {
$scope.name = 'World';
}

And when using modules it looks as follows:

angular.module('hello', [])
.controller
('HelloCtrl', function($scope){
$scope.name = 'World';
});

AngularJS itself defines the global angular namespace. There are various utility and convenience functions exposed in this namespace, and module is one of those functions. A module acts as a container for other AngularJS managed objects (controllers, services, and so on). As we are going to see shortly, there is much more to learn about modules than simple namespacing and code organization.

To define a new module we need to provide its name as the very first argument to the module function call. The second argument makes it possible to express a dependency on other modules (in the preceding module we don't depend on any other modules).

A call to the angular.module function returns an instance of a newly created module. As soon as we've got access to this instance, we can start defining new controllers. This is as easy as invoking the controller function with the following arguments:

  • Controller's name (as string)
  • Controller's constructor function

Globally-defined controller's constructor functions are only good for quick-code examples and fast prototyping. Never use globally-defined controller functions in larger, real life applications.

A module is defined now, but we need to inform AngularJS about its existence. This is done by providing a value to the ng-app attribute as follows:

<body ng-app="hello">

Forgetting to specify a module's name in the ng-app attribute is a frequent mistake and a common source of confusion. Omitting a module name in the ng-app attribute will result in an error indicating that a controller is undefined.

Collaborating objects

As we can see, AngularJS provides a way to organize objects into modules. A module can be used not only to register objects that are directly invoked by the framework (controllers, filters, and so on) but any objects defined by applications' developers.

Module pattern is extremely useful to organize our code, but AngularJS goes one step further. In addition to registering objects in a namespace, it is also possible to declaratively describe dependencies among those objects.

Dependency injection

We could already see that the $scope object was being mysteriously injected into controllers' instances. AngularJS is somehow able to figure out that a new scope instance is needed for a controller, and then creates a new scope instance and injects it. The only thing that controllers had to do was to express the fact that it depends on a $scope instance (no need to indicate how a new $scope object should be instantiated, should this $scope instance be a newly created one or reused from previous calls). The whole dependency management boils down to saying something along those lines: "To function correctly I need a dependency (collaborating object): I don't know from where it should be coming or how it should be created. I just know that I need one, so please provide it".

AngularJS has the dependency injection (DI) engine built in. It can perform the following activities:

  • Understand a need for a collaborator expressed by objects
  • Find a needed collaborator
  • Wire up objects together into a fully-functional application

The idea of being able to declaratively express dependencies is a very powerful one; it frees objects from having to worry about collaborating objects' lifecycles. Even better, all of a sudden it is possible to swap collaborators at will, and then create different applications by simply replacing certain services. This is also a key element in being able to unit test components effectively.

Benefits of dependency injection

To see the full potential of a system integrated using dependency injection, let's consider an example of a simple notification service to which we can push messages and retrieve those messages later on. To somewhat complicate the scenario, let's say that we want to have an archiving service. It should cooperate with our notifications service in the following way, as soon as the number of notifications exceeds a certain threshold the oldest notifications should be pushed to an archive. The additional trouble is that we want to be able to use different archiving services in different application. Sometimes dumping old messages to a browser's console is all that is needed; other times we would like to send expired notifications to a server using XHR calls.

The code for the notifications service could look as follows:

var NotificationsService = function () { this.MAX_LEN = 10; this.notificationsArchive = new NotificationsArchive(); this.notifications = []; }; NotificationsService.prototype.push = function (notification) { var newLen, notificationToArchive; newLen = this.notifications.unshift(notification); if (newLen > this.MAX_LEN) { notificationToArchive = this.notifications.pop(); this.notificationsArchive.archive(notificationToArchive); } }; NotificationsService.prototype.getCurrent = function () { return this.notifications; };

The preceding code is tightly coupled to one implementation of an archive (NotificationsArchive), since this particular implementation is instantiated using the new keyword. This is unfortunate since the only contract to which both classes need to adhere to is the archive method (accepting a notification message to be archived).

The ability to swap collaborators is extremely important for testability. It is hard to imagine testing objects in isolation without the ability to substitute real implementations with fake doubles (mocks). On the following pages of this article, we are going to see how to refactor this tightly-coupled cluster of objects into a flexible and testable set of services working together. In the process of doing so, we are going to take full advantage of the AngularJS dependency injection subsystem.

Registering services

AngularJS is only capable of wiring up objects it is aware of. As a consequence, the very first step for plugging into DI machinery is to register an object with an AngularJS module. We are not registering the object's instance directly, rather we are throwing object-creation recipes into the AngularJS dependency injection system. AngularJS then interprets those recipes to instantiate objects, and then connects them accordingly. The end effect is a set of instantiated, wired-up objects forming a running application.

In AngularJS there is a dedicated $provide service that allows us to register different recipes for objects creation. Registered recipes are then interpreted by the $injector service to provide fully-baked, ready-to-be-used object instances (with all the dependencies resolved and injected).

Objects that were created by the $injector service are referred to as services. AngularJS will interpret a given recipe only once during the application's lifecycle, and as a result will create only a single instance of an object.

Services created by $injector are singletons. There will be only one or instance of a given service per instance of a running application.

At the end of the day, AngularJS module just holds a set of object instances but we can control how those objects are created.

Values

The easiest way of having AngularJS to manage an object is to register a pre-instantiated one as follows:

var myMod = angular.module('myMod', []);
myMod.value('notificationsArchive', new NotificationsArchive());

Any service managed by AngularJS' DI mechanism needs to have a unique name (for example, notificationsArchive in the preceding example). What follows is a recipe for creating new instances.

Value objects are not particularly interesting, since object registered via this method can't depend on other objects. This is not much of the problem for the NotificationArchive instance, since it doesn't have any dependencies. In practice, this method of registration only works for very simple objects (usually expressed as instances of built-in objects or object literals).

Summary

We've covered a lot in this article. Our journey started by getting familiar with the AngularJS project and the people behind it. We've learned where to find the library's sources and documentation, so that we could write our first "Hello World" application. It is a pleasant surprise that AngularJS is very light-weight and easy to start with.

Most of this article, though was about building solid foundations for the rest of this article, we saw how to work with the AngularJS controllers, scopes and views, and how those elements play together. A big chunk of this article was devoted to the way AngularJS services can be created in AngularJS modules and wired up using dependency injection.

Resources for Article :


Further resources on this subject:


Mastering Web Application Development with AngularJS Build single-page web applications using the power of AngularJS with this book and ebook
Published: August 2013
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Pawel Kozlowski

Pawel Kozlowski has over 15 years of professional experience in web development and was fortunate enough to work with variety of web technologies, languages, and platforms. He is not afraid of hacking both at client side and server side and always searches for the most productive tools and processes.

Pawel strongly believes in free, open source software. He has been very committed in the AngularJS project and also is very active in the AngularJS community. He also contributes to Angular UI – the companion suite to the AngularJS framework, where he focuses on the Twitter's Bootstrap directives for AngularJS.

When not coding, Pawel spreads a good word about AngularJS at various conferences and meetups.

Peter Bacon Darwin

Peter Bacon Darwin has been programming for over two decades. He worked with .NET from before it was released; he contributed to the development of IronRuby and was an IT consultant for Avanade and IMGROUP before quitting to share his time between freelance development and looking after his kids.

Peter is a notable figure in the AngularJS community. He has recently joined the AngularJS team at Google as an external contractor and is a founder member of the AngularUI project. He has spoken about AngularJS at Devoxx UK and numerous London meetups. He also runs training courses in AngularJS. His consultancy practice is now primarily focused on helping businesses make best use of AngularJS.

Books From Packt


Instant AngularJS Starter [Instant]
Instant AngularJS Starter [Instant]

AngularJS Directives
AngularJS Directives

Instant Typeahead.js [Instant]
Instant Typeahead.js [Instant]

 Instant Ember.js Application Development How-to [Instant]
Instant Ember.js Application Development How-to [Instant]

Backbone.js Testing
Backbone.js Testing

Instant Handlebars.js [Instant]
Instant Handlebars.js [Instant]

Jasmine JavaScript Testing
Jasmine JavaScript Testing

Node Web Development
Node Web Development


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
w
i
y
P
x
v
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software