AngularJS: The Essentials

AngularJS Essentials

What Is AngularJS

AngularJS is a MVW (Model/View/Whatever) JavaScript framework that extends HTML through additional markup and produces more expressive and readable code. Its packed feature set make it a firm favourite for anyone developing SPAs or complex JavaScript based front ends.

Why AngularJS

Declarative User Interface

Through the use of AngularJS directives, filters and expressions we can build more declarative UIs. This means we don't have to get bogged down with how to bend and shape the DOM of a html template view to our will. We can just declare what we want and where. This not only reduces development time but also leaves us with clean, intuitive code.

2-Way Data Binding

The whole MVW or MVVM (Model/View/View-Model) approach that AngularJS is well known for is possible due to it's View-Model or Scope as it is referred to in these parts. An application's scope sits between its view and its controller. From one side the controller can add variables and functions to the scope which can in turn be used by (bound to) the view. Okay, so not that impressive right? However AngularJS can also bind the view, or to be more specific HTML form fields back to the scope. What does this mean? Live Preview baby! Any changes made to the model by the view will be seen by the controller. And vice versa, any changes made to the model by the controller will be immediately visible in the view. This means the scope model is considered the single source of truth.

Performance

One of the big advantages of the above is that it is all achieved without a page reload anywhere to be seen. After the initial page load of an AngularJS application, any changes to the view (model data, partial views, etc) are performed automatically. How does it do that you ask? You might imagine that AngularJS loads the DOM as a string and parses it, updating as it goes then returning the updated string back to the browser to reload the DOM. This wouldn't be very efficient however as every small change would require the whole DOM to be updated, again and again. What actually happens is that the AngularJS Compiler service traverses the actual DOM looking for directives and expressions and then links these elements to the scope. It achieves this by registering listeners to the elements and placing watches on the scope. Anything changes in either the view or the scope, the other is updated instantly without a complete reload of the DOM. End result? A very responsive, fast application.

Dependency Injection And Testing

An age old problem of testing lies with the dependencies of our code. If dependencies, such as a call to an API, are declared directly within the code itself, tests will forced to use the very same (and possibly production) API call. The problem here is that our tests need to use consistent data input so that the outcome of our code can be correctly predicted. In our example, using a call to a live API could provide data that has been modified between tests and so taint the expected outcome. We can of course mock the service with our own pre-set and consistent data (like a fixture), but how can we tell our code to switch services when testing? The easiest way of managing this is to inject the required service into our code module as a dependency. The code can then utilise whichever service we supply and so provide the flexibility we need. This approach doesn't only help with testing, it also makes our code infinitely more re-usable.

Thankfully, AngularJS was built with testing and the Separation Of Concerns (SoC) principal in mind from the outset and implements dependency injection throughout. Everything from components such as services, directives and filters to controllers and even module config can receive an array of dependencies to be injected. Nice!

...Oh And It Plays Nicely With jQuery

AngularJS comes with a lite version of jQuery aptly called jqLite built in. So if you are a fan of jQuery selectors for example, you can continue to use these within your AngularJS code. Should you discover you need to use a full blown version of jQuery, just make sure it is loaded before AngularJS and it will be used instead of jqLite.

Getting Started

Download or link to the CDN of the latest version of AngularJS (1.3.8 at the time of writing) in our html template (index.html):

<!DOCTYPE html>
<html> 
<head>
	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
</head>
<body>
...
</body>
</html>

Modules

In keeping with SoC, AngularJS applications are built in a modular fashion to promote code re-use and more maintainable and easily testable code.

Let's create an example AngularJS application module (app.js):

var app = angular.module( 'myApp', [] );

The first argument is the name of the module. The second is an array of dependencies to be injected (of which we don't have any in this basic example).

Now we include the module file in our html template:

<head>
	...
	<script type="text/javascript" src="app.js"></script>
	...
</head>

And finally we need to bootstrap the application using our very first built-in directive ng-app (more on directives later):

<html ng-app="myApp">
...
</html>

Let's Meet The AngularJS Family

Expressions

AngularJS allows us to bind JavaScript-like expressions directly into a html template. The expressions can perform numerical and string operations or can refer to a variable/model within the scope.

<p>1 + 2 = {{ 1 + 2 }}</p>  
  
<p>Hello {{ customer.firstName }}</p>

Controllers

An application's business logic is defined within its controllers. We can define our application's behaviour here as well as set the initial state of the controller's new child scope.

First we need to create a controller and attach it to our application module (app) using the controller() method. The first argument is where we define the name of the controller and the second is the controller's constructor function (along with its dependencies):

var app = angular.module( 'myApp', [] );  
  
app.controller( 'CustomerController', [ '$scope', function( $scope ) {  
  
	$scope.customer = {
		firstName: 'Peter',
		lastName: 'Parker',
		photo: 'pparker.jpg',
		isMember: true
	};  
  
}]);

As you can see above, we have also initialised a 'customer' object within the controller's scope. In real world examples of course this sort of data would probably be provided by an API service (injected as a dependency of the controller) rather than hard coded like this.

Also note that whilst we are adding our controller to the same file as the module here (app.js), you can and probably should define your controllers in separate files (to be covered in a later article).

Now to access our controller's scope (and all the values, objects and methods etc. associated with it) from within a html template, we must attach the controller to some relevant html element using another of the AngularJS built-in directives:

...
<div ng-controller="CustomerController">
	<p>Dear {{ customer.firstName }} {{ customer.lastName }},</p>
</div>
...

Note that we can only access the controller's scope from within the bound element. If we tried to place {{ customer.firstName }} anywhere outside of the div bound to the CustomerController, it would be ignored.

Built-in Directives

As we have already seen earlier, AngularJS comes ready to roll with built-in directives. These directives allow us to extend standard HTML to help us build web applications. All AngularJS built-in directives use the namespace 'ng' at the start i.e. ngApp, ngController, etc. (note the camelCase here). However, when we use the directives in html the camelCase is substituted for hyphenation i.e. ng-app, ng-controller, etc.

Below is a selection of some of the most commonly used directives:

ng-app
Used to auto-bootstrap an AngularJS application and also defines the root element of the application by whichever element the directive is placed on (usually <html> or <body>).

<html ng-app="myApp">

ng-controller
As we saw earlier, this directive is used to attach an existing AngularJS controller to a element within the html template. Remember, the controller's scope will only be available within the element that it is bound to.

<div ng-controller="CustomerController">
...
</div>

ng-show / ng-hide / ng-if
All three of these directives accept an expression to be evaluated. The first two are self explanatory i.e. they will show or hide whatever element they are attached to if their expression evaluates to true. The ng-if directive is similar to these but with one main difference. Whilst ng-show and ng-hide merely use CSS to show/hide the element, ng-if actually removes the element (and its contents) from the DOM if its expression evaluates to false and recreates it if true.

<div ng-if="customer.isMember">
...
</div>  
  
<div ng-hide="!customer.isMember">
...
</div>

ng-click
Attaches a click handler to an element. When the bound element is clicked, the method passed to the handler is run. The method must of course first be defined in the controller and added to the scope.

app.controller( 'CustomerController', [ '$scope', function($scope) {  
  
	$scope.updateCustomer = function() {
		...
		some code to save the customer changes back to the API
		...
	};  
  
}]);
<button ng-click="updateCustomer()">Save Changes</button>

ng-src
The problem with dynamically setting the source of an <img> element is that the data in the scope won't be available for the browser to pre-load as AngularJS hasn't parsed the DOM yet. AngularJS provides a solution with the ng-src directive. As the <img> will initially have no src, no attempt will be made to pre-load. Once the DOM has been parsed, AngularJS can set the src.

Won't work (well it might but only after an additional request):

<img src="http://www.example.com/photos/{{ customer.photo }}" />

Will work:

<img ng-src="http://www.example.com/photos/{{ customer.photo }}" />

ng-repeat
So far so good, but ng-repeat steps up the excitement levels! No? Just me then. This powerful directive lets us iterate over a collection of items, such as an array of objects, applying a template to each item. In other words, a big time saver.

Let's say we have an array of fruit objects in a controller:

$scope.fruits = [
        {
            name: 'Bananas',
            stock: 5
        },
        {
            name: 'Apples',
            stock: 2
        },
        {
            name: 'Oranges',
            stock: 3
        }
];

Now we want to list those healthy snacks out in a html template along with the current stock level:

<ul>
	<li ng-repeat="fruit in fruits">{{ fruit.name }} ({{ fruit.stock }})</li>
</ul>

Yep that's it! I told you it was cool. We attach the ng-repeat directive to the element we wish to replicate for every item, for example the <li> element. Each item in the fruits array from our controller scope is assigned its own scope, in this case named fruit. We can then insert our AngularJS expressions to place our data where required.

Outputs:

  • Bananas (5)
  • Apples (3)
  • Oranges (3)

Filters

Once again AngularJS gets us started with a bunch of built-in filters that allow us to create subsets of, limit, order or format the output of an expression. They are preceded by the | symbol (to pipe our data through the filter) followed by the filter component name. This is then followed by a : and any filter arguments.

{{ expression | filter:arguments }}

Here is a selection of a few of them:

currency
Format the expression to a currency with the provided symbol:

{{ 1234 | currency:"&pound;" }}

Outputs: £1,234.00

date
Formats the expression (a date object, or milliseconds for example) to the provided date format:

{{ 1420070401000 | date:"dd/MM/yyyy HH:mm:ss" }}

Outputs: 01/01/2015 00:00:01

filter
Creates and returns a subset array from the provided filter expression array (such as an array of objects used in a ng-repeat) based on the expression argument (a string, a pattern object or a predicate function). Let's use our earlier ng-repeat example and apply a couple of different filters.

String example:

<ul>
	<li ng-repeat="fruit in fruits | filter:'an'">{{ fruit.name }} ({{ fruit.stock }})</li>
</ul>

Outputs:

  • Bananas (5)
  • Oranges (3)

Pattern Object example:

<ul>
	<li ng-repeat="fruit in fruits | filter:{ name:'es', stock:3 }">{{ fruit.name }} ({{ fruit.stock }})</li>
</ul>

Outputs:

  • Apples (3)
  • Oranges (3)

Predicate Function example (requires the function to be defined in the controller's scope):

$scope.filterByLowStock = function(fruit) {
	return fruit.stock < 5;
};
<ul>
	<li ng-repeat="fruit in fruits | filter:filterByLowStock">{{fruit.name}} ({{fruit.stock}})</li>
</ul>

Outputs:

  • Apples (3)
  • Oranges (3)

Custom
Once you get the hang of the built-in offerings, have a go at creating your own custom filter.

Forms & Models

There was a significant omission from the earlier introduction to AngularJS built-in directives by the name of ng-model. This directive is the final piece in the 2 Way Data Binding puzzle and let's us bind html form fields to a model defined in the controller's scope. Assuming we use the earlier defined CustomerController, let's take a look at a basic example:

<p>Customer name: {{customer.firstName}} {{customer.lastName}}</p>  
  
<form>
	<input type="text" ng-model="customer.firstName" />
	<input type="text" ng-model="customer.lastName" />
</form>

As you can see we have used ng-model to bind a couple of input fields to properties of our customer object. We are also displaying these properties above to illustrate the live preview. Now if we change either of the input field values (note the default value is set from the existing initialised model), this will update the model (customer object) in the scope which will in turn update the view and display our changes above.

Note that there is no data retention at this stage. A refresh of the page and we are back where we started. This would require a function to be defined in the controller that saves our changes back to data source such as an API. Check out the ng-click example above for how we might go about this.

Wrap Up

Hopefully you now have a good understanding of the what, why and the beginnings of the how of AngularJS and feel inspired to have a go yourself. Tweet me up and let me know how you get on.

I have planned a bunch of follow up articles to delve further into AngularJS so check back soon.