0% found this document useful (0 votes)
51 views

AngularJS Tutorial

This document provides an introduction and overview of AngularJS for beginners. It discusses the history and origins of AngularJS in Model-View-Controller (MVC) patterns. It then covers key AngularJS concepts like directives, bindings, expressions and shows how to set up a basic AngularJS application with directives like ng-app, ng-controller and ng-bind. Sample code is provided to demonstrate integrating these directives and bindings to display data from a controller.

Uploaded by

Mycel Calanza
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
51 views

AngularJS Tutorial

This document provides an introduction and overview of AngularJS for beginners. It discusses the history and origins of AngularJS in Model-View-Controller (MVC) patterns. It then covers key AngularJS concepts like directives, bindings, expressions and shows how to set up a basic AngularJS application with directives like ng-app, ng-controller and ng-bind. Sample code is provided to demonstrate integrating these directives and bindings to display data from a controller.

Uploaded by

Mycel Calanza
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 22

AngularJS Tutorial: A Beginner’s Guide to AngularJS

By Mike Godfrey for Udemy

TABLE OF CONTENTS: CLICK TO JUMP TO A SPECIFIC SECTION

Introduction

History is important
MV*
Setup

Directives

ng-app
ng-controller
ng-bind

Bindings

Expressions
Two-way bindings

Directives again

ng-repeat

limitTo

Custom directives

Injection

Minimal theory
Dependency annotations
Custom injectables
Value services
Factory services

Directives continued

ng-click
ng-changed

Server integration

$http
$httpBackend
Promises
$resource
$httpBackend again

Summary

Introduction
Since I first started using AngularJS several years ago, it has become the most popular single-page application (SPA)
framework for the JavaScript language. AngularJS makes it easy to solve simple problems fast, yet has enough features to
enable development teams to build complex large-scale applications.

This tutorial aims to introduce a complete beginner to AngularJS by explaining fundamental concepts and building a
sample application. The application will be a simplified admin area for a multi-author blog.

History is important

I believe a limited appreciation of history is important. Often in software, good ideas are a twist or improvement on
something that has been done before, made possible by some other technology improvement(s). In the case of AngularJS,
the vast performance improvements made to browser JavaScript engines has made ambitious SPA frameworks possible.

AngularJS is an adaptation of the Model-View-Controller (MVC) pattern first created in 1979. MVC helps developers to
separate the concerns involved in building user interfaces. Jeff Atwood of Coding Horror wrote of an example that
we’re all very familiar with, a static webpage:

 HTML acting as the model, the content (data) for the page
 CSS acting as the view, how that content should be presented
 The browser acting as the controller, combining the model and the view to render the page

MV*

Subtle variations to the MVC pattern have come in recent years, none more so than by JavaScript SPA frameworks. For
this reason, the MV* phrase has been coined as a way of describing ‘whatever’ variation works for you.

So although it’s definitely worth understanding the origins of MVC, keep in mind that AngularJS is MV* and does not
implement MVC strictly.

Setup

This tutorial will use the stable version of AngularJS at the time of writing (1.4.4). It also intends to only include the bare
minimum required to run an AngularJS application.

To get up and running, download an uncompressed copy of AngularJS from angularjs.org. Create a file structure as
follows with your favorite text editor:

Initial file layout

Edit the contents of index.html as follows:

<!DOCTYPE html>
<html>
<head>
<title>Udemy tutorials - admin area</title>
</head>
<body>

<script src="angular.js"></script>
</body>
</html>

Note that in the above snippet we’re serving angular.js from the local file system. This saves us from having to
introduce any additional moving parts and is adequate for our purposes. Open index.html in your favorite browser and,
although you’ll see nothing but a white screen, check to make sure that you don’t have any errors in your browser’s
developer tools/console.

We will add additional .js files as we progress through the tutorial.

Directives

Considering the fact that I discussed MVC in the introduction, it would be very tempting to structure this tutorial by the
AngularJS implementation of models, views and controllers. But there is a construct so fundamental to AngularJS that to
not start with it would be an error.

That construct is directives. AngularJS describes itself as “HTML enhanced for web apps.” Directives are what facilitate
this enhancement; they extend the capability of HTML elements.

I often find analogies to be a useful tool for explaining abstract concepts, such as directives. Consider a homeowner who is
having some building work completed on their house. The team of laborers required to complete any non-trivial building
project is varied in role and might include:

 a bricklayer
 an outfitter
 a plumber

The types of capability directives extend HTML elements with can be categorized similarly:

 structural
 decorative
 plumbing

If we keep the analogy going a moment longer, HTML is the house and directives are the team of laborers.

Previously I introduced a sample application that we will build during this tutorial. Let’s build upon that by displaying an
article title contained within a plain JavaScript object:

var article = {
title: "Learn AngularJS"
};

Edit index.html to contain the ng-app, ng-controller and ng-bind directives as follows:

<!DOCTYPE html>
<html ng-app="udemyAdmin">
<head>
<title>Udemy tutorials - admin area</title>
</head>
<body ng-controller="articleCtrl">
<div ng-bind="title"></div>
<div ng-bind="getTitle()"></div>

<script src="angular.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
</body>
</html>

You’ll notice that two .js files have appeared. There is a small amount of JavaScript required to make the article title
appear in the <div>s and app.js and controllers.js is where it happens. Before we create them, I want to briefly
discuss the three directives introduced in the above snippet.

ng-app

ng-app is a plumbing directive. AngularJS looks for which HTML element has the ng-appdirective attached to it and
uses it to bootstrap an application. ng-app can be placed on any HTML element, not just <html>, and connects a module
(in our case, ‘udemyAdmin’) to it. Modules will be covered shortly.

ng-controller

ng-controller is a plumbing directive. It connects a model and any related behaviors to a branch of the HTML tree
made available by the usage of ng-app.

ng-bind

ng-bind is a plumbing directive. It connects a model property or the result of a model behavior to the text content of an
HTML element. In our example, the title property and getTitle behaviour is available because ng-bind has been
used on a <div> which is a child of <body> (where ng-controller has been used).

Edit the file structure to match the following:

New file layout

Edit the contents of app.js as follows:

angular.module('udemyAdmin', []);

In the above snippet, we first create a module called ‘udemyAdmin’. This module is created with no dependencies
(denoted by the empty array). Modules allow us to partition different parts of an application anytime we wish; this is
useful in larger applications. For example, a full Udemy blog application might be partitioned as follows:

angular.module('udemy', ['udemy.courses', 'udemy.authors', 'udemy.students']);


angular.module('udemy.courses', ['udemy.courses.archived', 'udemy.courses.current']);
// and so on

Edit the contents of controllers.js as follows:


angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
var title = "Learn AngularJS";

$scope.title = title;
$scope.getTitle = function() {
return title;
};
});

In the above snippet, we first retrieve the ‘udemyAdmin’ module (angular.module is dual operation based on the
number of arguments) and then register a controller within it. $scope is a core AngularJS component available for
injection to controllers (we cover injection in a later section of this tutorial). $scope is the model exposed
by articleCtrl; we add properties and behaviors we want to be available for use in our HTML.

Although app.js and controllers.js are very small at the moment, they will grow as this tutorial progresses and it is
useful to refer to snippets of code by file name.

Bindings

In the previous section of this tutorial, we introduced ng-bind as a way to connect controller models to the text content of
HTML elements. There is an alternative, non-directive way of achieving this, namely double curly bracket notation or {{
}}. To use it in index.html we would edit the <div>s as follows:

<div>{{title}}</div>
<div>{{getTitle()}}</div>

Typically {{ }} is used over ng-bind as it is less verbose (4 characters versus 10).

ng-bind does have one distinct advantage, though. It prevents any temporary flashing of curly braces, which can happen
if there is a gap between the browser rendering HTML and AngularJS executing. The ng-cloak directive is an alternative
solution, but we won’t go into details in this tutorial.

Expressions

Both {{ }} and ng-bind require an AngularJS expression. AngularsJS expressions are similar in concept to
JavaScript expressions but with subtle differences that I’ll highlight with examples.

We could change the expressions used in index.html (currently title and getTitle()) to any of the following:

 title + 1 – expressions can have operators. Here we are operating on a string, so 1 is converted to “1” (as per
normal JavaScript) resulting in “Learn AngularJS1”.
 title + ': ' + description – expressions are forgiving. description doesn’t exist, but the result is
“Learn AngularJS: ” rather than “Learn AngularJS: undefined” as you might expect.
 title + (description ? ': ' + description : '') – conditionals are allowed but only in ternary
form, so the result here is just “Learn AngularJS”.

Curly brackets are not allowed in AngularJS expressions, which means that blocks (e.g., if () { }) and function
declarations (e.g., function() { }) are ruled out.

The recommendation is to have a model behavior instead of complex expressions. In general, it’s a good idea to keep your
expressions looking attractive.

Two-way bindings

The connections made between controller models and HTML elements are more commonly known as ‘bindings’. All
bindings in AngularJS are two-way. This means that any updates to bound model properties are automatically reflected in
the HTML. Even at this early stage, our sample application has two-way bindings. It just doesn’t look like it because there
is no binding that will update the model. Let’s change that now so that we can properly highlight two-way binding.

Edit index.html as follows:

<!DOCTYPE html>
<html ng-app="udemyAdmin">
<head>
<title>Udemy tutorials - admin area</title>
</head>
<body ng-controller="articleCtrl">
<div ng-bind="title"></div>
<div>{{title}}</div>
<input type="text" ng-model="title" />

<script src="angular.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
</body>
</html>

In the above snippet, we’ve introduced an HTML text input element and attached an ng-model directive to it. ng-
model listens for DOM events (e.g., keyup) raised by the element, reads its value and writes it to the bound model
property specified in the expression (i.e., title). The text content of the <div>s updates thanks to the binding created
by ng-bind and {{ }}. Try this out and witness the magic of two-way binding.

Directives again

In the first directives section, I introduced an analogy (a team of laborers completing some building works) and used it to
begin categorizing directives as one of three types:

 structural
 decorative
 plumbing

Unfortunately, I then proceeded to only introduce plumbing directives (ng-app, ng-controller and ng-bind). Let’s
fix that now by returning to directives and discussing ng-repeat.

ng-repeat

Most applications built with AngularJS are likely to be data-driven; reading, creating, updating and deleting data. Real-
world data is often plural in nature – for example:

 walking into your local foreign exchange provider to buy currency for your next holiday. You’ll likely be presented
a board of rates for the most popular currency pairings.
 visiting your local bookstore and browsing your favorite niche. You’ll likely be presented with a range of
choices for a book to leaf through.

ng-repeat is a structural directive because it modifies the “bricks and mortar” of our HTML. At a simple level, it creates
the HTML element it is attached to multiple times based on the contents of a model collection property, e.g., an array.

Lets see how ng-repeat can help us display an array of articles. Edit index.html to the following:

<!DOCTYPE html>
<html ng-app="udemyAdmin">
<head>
<title>Udemy tutorials - admin area</title>
</head>
<body ng-controller="articleCtrl">

<div ng-repeat="article in articles" ng-bind="article.title"></div>

<ul>
<li ng-repeat="article in articles">
<p>{{article.title}}</p>
</li>
</ul>

<script src="angular.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
</body>
</html>

In the snippet above, article in articles is much like a traditional JavaScript for…inloop, with article being the
name of the variable assigned to on each iteration of articles. article is then available for use in other directives and
bindings, either on that HTML element or a descendant (the snippet shows both).

Edit controllers.js so that an appropriate model property is available as follows:

angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
$scope.articles = [
{ title: "Learn AngularJS" },
{ title: "JavaScript closures explained!" }
];
});

All being well, you’ll see the same two article titles displayed twice – inside <div> and <p> elements, respectively.

ng-repeat is a very powerful directive. We’ve used it to simply create an HTML element per item in a static array. It is
more likely that you’ll use ng-repeat with dynamic collections – for example, live feeds of buy and sell prices for foreign
exchange currency pairings. ng-repeat will track changes in dynamic collections and, in most cases, behave as you
would expect, creating, editing and destroying HTML elements appropriately.

limitTo filter

We can further demonstrate the power of ng-repeat by combining it with a filter. Filters could demand a section of this
tutorial to themselves, but in general they are applied to expressions, using the | operator, and format the result.

AngularJS comes with a handful of built-in filters, including limitTo. limitTo will format the result of the expression
we have used with ng-repeat by returning a specified number of articles from an optional starting index.

Edit index.html as follows:

<!DOCTYPE html>
<html ng-app="udemyAdmin">
<head>
<title>Udemy tutorials - admin area</title>
</head>
<body ng-controller="articleCtrl">

<div ng-repeat="article in articles | limitTo:1 " ng-bind="article.title"></div>

<ul>
<li ng-repeat="article in articles | limitTo:1:1">
<p>{{article.title}}</p>
</li>
</ul>

<script src="angular.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
</body>
</html>

In the above snippet, both ng-repeat expressions include a contrived usage of the limitTo filter (we only have two
articles thus far, so limiting to one seems pretty pointless).

The first expression is limited to one article, but we haven’t specified a starting index, so 0 is used. This results in HTML
of <div>Learn AngularJS</div>.

The second expression is also limited to one article, but this time we have specified a starting index. This results in HTML
of <p>JavaScript closures explained!</p>.

Custom directives

AngularJS comes with a handful of structural directives, such as ng-include for fetching and displaying external HTML
fragments, but ng-repeat is the most significant.

This is beyond the scope of this tutorial, but it is possible to create your own custom directives of any type (plumbing,
structural or decorative). To whet your appetite for what might lie ahead in your own AngularJS journey, check out the
directives in the excellent Angular Material library or my own angular-charts library.

You will probably note that we still haven’t discussed decorative directives. That will come later in one final directives
section.

Injection

In the first directives section, we mentioned injection, stating that we would cover it in a later section. Injection might get
a little complicated, but I’ll do my best to keep it concise and understandable.

Minimal theory

Dependency injection is a common software design pattern that is frequently used in any non-trivial application. By
writing an AngularJS application, you will use dependency injection, even if you’re not aware of it. As such, it makes sense
to learn a little of the theory behind the pattern.

The essence of the pattern is to separate the responsibilities of construction and usage of dependencies. In this tutorial, we
have already seen an example of this, as follows:

angular.module('udemyAdmin').controller('articleCtrl', function($scope) {
$scope.title = "Learn AngularJS";
});

In this snippet, articleCtrl is dependent on $scope in order to make model properties and behaviors available for
binding. We do not know how $scope is constructed by AngularJS (nor do we really care), as long as we can use it
within articleCtrl.

If we were to construct $scope ourselves, in a very simplified form it might look something like the following:
angular.module('udemyAdmin').controller('articleCtrl', function() {
var $scope = new angular.Scope();
$scope.title = "Learn AngularJS";
});

In this snippet, the responsibility for the construction and use of the $scope dependency is solely with articleCtrl. A
principle of good software design is for a component, such as articleCtrl, to do one thing and to do it well.

Dependency annotations

AngularJS needs assistance to know what to inject into the callbacks we provide to certain functions, such
as controller(). In the examples we’ve seen so far, AngularJS uses the argument name, so if we were to make a typo
such as £scope AngularJS wouldn’t know what to inject.

Although typos are possible, they’re fairly quick to spot (via errors in your browser’s developer tools/console) and fix. A
more significant problem is associated with the minification of scripts comprising our AngularJS applications. We’ve all
seen the output of most minifers: unintelligble single-character variable names everywhere! Minifers will
rename $scope, for example, and AngularJS won’t get any injection assistance.

To be able to use minifers, we can use dependency annotations as follows:

angular.module('udemyAdmin').controller('articleCtrl', ['$scope', function($scope) {


$scope.title = "Learn AngularJS";
}]);

In the above snippet, we’re now passing an array as the second parameter to controller(). The last item in the array
needs to be our original callback, while the first items are the string names of the dependencies to be injected. A
rudimentary minifier might output the above snippet as follows:

a.m('udemyAdmin').c('articleCtrl', ['$scope', function(s) {


s.t = "Learn AngularJS";
}]);

Strings can’t be renamed, so AngularJS can use dependency annotations to get the injection assistance it needs. There’s a
small caveat to this approach: the array item order and callback argument order must be in sync. Additionally, we’re still
prone to errors from typos, but overall the benefit of minification far outweighs these caveats in any larger application.

However, we won’t use dependency annotations in this tutorial, preferring instead to keep snippets lean and focused.

Custom injectables

AngularJS offers four techniques for registering our own injectable components, much like $scope, as follows:

 constant
 value
 factory
 service

Injectable components are known as services in AngularJS speak, so it’s a shame that they decided to call one of the
techniques the same.

We will discuss the more common techniques that I have used myself and have seen used by others, namely value and
factory. Constant and service are less common and more complicated in use; as such, I consider them beyond the scope of
this tutorial.
Value services

A value service allows us to register injectable values (be it strings, numbers, objects, arrays or functions). They allow us to
define a value that can’t be changed, much like a constant in other programming languauges. The fact that there is also a
constant service is another unfortunate naming choice by AngularJS. The difference between the two is associated with
the configuration life cycle of an AngularJS application, rather than anything to do with the value registered.

A value service allows us to register a value once and use it many times (via injection). Consider the limitTo filter from
the “Directives again” section of this tutorial. We could introduce a pageSize value service, inject it
into articleCtrl and use it as the default number of articles to display. pageSize could also be reused in other areas of
the application, such as administering a subset of a large number of categories.

Edit the file structure to match the following:

New file layout

Edit the contents of services.js as follows:

angular.module('udemyAdmin').value('pageSize', 2);

In the above snippet, we create the most basic of value services, registering the value 2 for the pageSize injectable
component.

Edit the contents of controller.js as follows:

angular.module('udemyAdmin').controller('articleCtrl', function($scope, pageSize) {


$scope.articles = [
{ title: "Arduino Tutorial" },
{ title: "After Effects Tutorial" },
{ title: "Django Tutorial" }
];

$scope.numArticles = pageSize;
});

In the above snippet, articleCtrl now has a dependency on pageSize, so the value 2 is injected in. We then store it
as $scope.numArticles for use in our HTML.

Finally, edit index.html as follows:

<!DOCTYPE html>
<html ng-app="udemyAdmin">
<head>
<title>Udemy tutorials - admin area</title>
</head>
<body ng-controller="articleCtrl">

Number of articles to display: <input type="text" ng-model="numArticles" />

<ul>
<li ng-repeat="article in articles | limitTo:numArticles">
{{article.title}}
</li>
</ul>

<script src="angular.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
<script src="services.js"></script>
</body>
</html>

In the snippet above, limitTo is part of an expression and can therefore include $scopeproperties. We’ve replaced the
hard-coded number of articles to limit to with numArticles. Additionally, we’ve attached ng-model to a new HTML
text input. This is to allow a user to override the default provided by pageSize, should they wish to do so.

Factory services

A factory service also allows us to register injectable values. The key difference is that the injectable value is the return
value of a function that can itself be injected with dependencies. This is definitely best explained with an example.

Edit services.js as follows:

angular.module('udemyAdmin')

.value('pageSize', 2)

.value('calculateCategoryPercentage', function(articles) {
var availableCategories = ['tutorial', 'graphics', 'hardware'];
var uniqueCategories = [];

articles.forEach(function(article) {
article.categories.forEach(function(category) {
if (uniqueCategories.indexOf(category) == -1) {
uniqueCategories.push(category);
}
});
});

return Math.floor(100 * (uniqueCategories.length / availableCategories.length));


});

In the above snippet, we have registered calculateCategoryPercentage as a value service that will calculate a
percentage of used categories from an array from articles. The details of how (i.e., the nested looping) isn’t important, but
note how we have hard-coded the available categories in a local variable.

Now edit services.js as follows:

angular.module('udemyAdmin')
.value('pageSize', 1)

.value('availableCategories', ['tutorial', 'graphics', 'hardware'])

.factory('calculateCategoryPercentage', function(availableCategories) {

return function(articles) {
var uniqueCategories = [];

articles.forEach(function(article) {
article.categories.forEach(function(category) {
if (uniqueCategories.indexOf(category) == -1) {
uniqueCategories.push(category);
}
});
});

return Math.floor(100 * (uniqueCategories.length / availableCategories.length));


};
});

In the snippet above, we have registered the previously hard-coded available categories as a value service, enabling them
to be used elsewhere in the application via injection. We have then registered calculateCategoryPercentage as a
factory service, a function that gets called once with its dependency (availableCategories) injected and then returns
the same function as the value service version.

To see how to use calculateCategoryPercentage, edit controllers.js as follows:

angular.module('udemyAdmin').controller('articleCtrl', function($scope, pageSize,


calculateCategoryPercentage) {
$scope.articles = [
{ title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
{ title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
{ title: "Django Tutorial", categories: ['tutorial'] }
];

$scope.numArticles = pageSize;

$scope.categoryPercentage = calculateCategoryPercentage($scope.articles);
});

In the snippet above, calculateCategoryPercentage is injected into the controller and then invoked with our hard-
coded list of articles. It’s important to note that availableCategories could also be injected into the controller, and in
a later section we will do so.

As with other examples we’ve seen, $scope.categoryPercentage can be used in index.htmlwith a simple binding,
such as:

<span>Percentage of categories used: {{categoryPercentage}}</span>

Value and factory services are a more advanced technique, but their use allows us to achieve a level of decoupling in our
AngularJS applications that will greatly improve the speed of implementing any future requirements.

Directives continued
In this last directives section, we again return to our analogy (a team of laborers completing some building works), as we
have yet to cover any decorative directives.

Rather than strictly being about styling and CSS, by decorative I mean in the sense of HTML elements being decorated
with additional behaviour. This is like having a new kitchen fitted, which isn’t stuctural “bricks and mortar”, nor is it
plumbing as the pipes have already been laid and are waiting to be used. But a new kitchen definitely gives a house
additional behavior, especially if it has one of those fancy taps producing instant boiling water.

Lets now look at a couple of example decorative directives, namely ng-click and ng-checked.

ng-click

ng-click decorates HTML elements with the ability to invoke a model behaviour when they are clicked, which I suspect
is fairly self-explanatory.

In the previous section, we introduced a factory service calculateCategoryPercentage and used it with a hard-
coded list of articles. Let’s make our application a little more interesting by allowing new articles to be created.

Edit index.html as follows:

<!DOCTYPE html>
<html ng-app="udemyAdmin">
<head>
<title>Udemy tutorials - admin area</title>
</head>
<body ng-controller="articleCtrl">

<input type="text" ng-model="newTitle" placeholder="Enter article name..." />


<button name="Add" ng-click="addArticle()">Add</button>

<hr />

Number of articles to display: <input type="text" ng-model="numArticles" />

<ul>
<li ng-repeat="article in articles | limitTo:numArticles">
{{article.title}}
</li>
</ul>

<span>Percentage of categories used: {{categoryPercentage}}</span>

<script src="angular.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
<script src="services.js"></script>
</body>
</html>

In the snippet above, we’ve added two new HTML elements. The text input element will update a model
property newTitle via the ng-model directive (which has been discussed in a previous section on “Bindings”). The
button element will invoke a model behaviour addArticle when clicked via ng-click.

Edit controllers.js as follows:

angular.module('udemyAdmin').controller('articleCtrl', function($scope, pageSize,


calculateCategoryPercentage) {
$scope.articles = [
{ title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
{ title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
{ title: "Django Tutorial", categories: ['tutorial'] }
];

$scope.newTitle = '';

$scope.addArticle = function() {
$scope.articles.push({ title: $scope.newTitle, categories: [] });
};

$scope.numArticles = pageSize;

$scope.categoryPercentage = calculateCategoryPercentage($scope.articles);
});

In the above snippet, addArticle uses $scope.newTitle when invoked to push a new article
onto $scope.articles. Note that the new article has an empty categories array.

ng-checked

Our blog admin application needs to allow articles, new and old alike, to be re-categorized. Wouldn’t it be great if when
this happens $scope.categoryPercentage is re-calculated? This can be accomplished with another usage of ng-
click and a new directive ng-checked. Let’s explore how now.

Edit controllers.js as follows:

angular.module('udemyAdmin').controller('articleCtrl', function($scope,
calculateCategoryPercentage, pageSize, availableCategories) {

$scope.categories = availableCategories;

$scope.articles = [
{ title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
{ title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
{ title: "Django Tutorial", categories: ['tutorial'] }
];

$scope.$watch('articles', function(articles) {
$scope.categoryPercentage = calculateCategoryPercentage(articles);
}, true);

$scope.containsCategory = function(article, category) {


return article.categories.indexOf(category) >= 0;
};

$scope.toggleCategory = function(article, category) {


var index = article.categories.indexOf(category);

if (index == -1) {
article.categories.push(category);
} else {
article.categories.splice(index, 1);
}
};

$scope.newTitle = '';

$scope.addArticle = function() {
$scope.articles.push({ title: $scope.newTitle, categories: [] });
};

$scope.numArticles = pageSize;
});

We’ve introduced quite a lot in the above snippet. Let’s discuss each in turn.

We previously mentioned that we would find another use for availableCategories. It is now also injected
into articleCtrl and set as $scope.categories.

When we create bindings in our HTML (e.g., via ng-bind, {{ }}, ng-model and many others), we are implicitly
creating watches. At a simplified level, whenever AngularJS detects something that might affect an application (be it a
browser event, HTTP response, and many others), it checks all of its watches against their previous values and updates
bindings if there is a difference. We use $scope.$watch to create a manual watch of the articles array and re-
calculate $scope.categoryPercentage if there is a difference. The third parameter to $scope.$watch indicates that
we want a “deep” watch. For more information, please have a look at this excellent StackOverflow answer.

Finally, we introduce two new model behaviors for interacting with an article’s
categories. $scope.containsCategory simply saves us from having an ugly expression in our
HTML. $scope.toggleCategory either adds or removes a category based on whether an article is already categorized
as such.

To see all of this in action, edit index.html as follows:

<!DOCTYPE html>
<html ng-app="udemyAdmin">
<head>
<title>Udemy tutorials - admin area</title>
</head>
<body ng-controller="articleCtrl">

<input type="text" ng-model="newTitle" placeholder="Enter article name..." />


<button name="Add" ng-click="addArticle()">Add</button>

<hr />

Number of articles to display: <input type="text" ng-model="numArticles" />

<div ng-repeat="article in articles | limitTo:numArticles">


<p>{{article.title}}</p>
<label ng-repeat="category in categories">
<input type="checkbox"
ng-checked="containsCategory(article, category)"
ng-click="toggleCategory(article, category)" />
{{category}}
</label>
</div>

<span>Percentage of categories used: {{categoryPercentage}}</span>

<script src="angular.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
<script src="services.js"></script>
</body>
</html>
In the above snippet, we come across the usage of ng-click and ng-checked mentioned a little while ago. We’ve
introduced a second ng-repeat loop to create a checkbox for each category. This ng-repeat loop is nested and, as such,
creates the set of checkboxes for each article. ng-checked is used to ensure that the underlying state of the HTML
checkbox element is kept in sync with the model (this is very similar to ng-model). ng-click is used to
invoke toggleCategory with the appropriate article and category.

If you try this out, you should see the percentage of categories used being updated as you categorize articles.

Server integration

The aim of this section is to discuss two services AngularJS provides for integrating with a server,
namely $http and $resource. We’ll look at $http first and $resource second. $resource is essentially a RESTful
extension of $http.

Previously we introduced a feature to our blog admin application that calculated the percentage of used categories. We
originally used this feature to look at how we can create our own injectable components. We’ll now use it again to
discuss $http.

$http

The requirements for our blog admin application have changed, and we now need to fetch the categories from a server.

The simplest way to achieve this is to use $http directly in articleCtrl.

The contents of articleCtrl became quite lengthy in the previous section, so the following two snippets are intended to
be illustrative.

angular.module('udemyAdmin').controller('articleCtrl', function($scope, $http,


calculateCategoryPercentage) {

$scope.articles = [
{ title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
{ title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
{ title: "Django Tutorial", categories: ['tutorial'] }
];

$scope.categoryPercentage = 0;

$http.get('/categories').then(function(response) {
$scope.categoryPercentage = calculateCategoryPercentage($scope.articles,
response.data);
});
});

In the above snippet, we issue an HTTP request to the URL ‘/categories’ and, if it is successful, we pass the response data
to calculateCategoryPercentage. There are two key things to note here.

First, there will be a delay while the server responds. During this delay, the $scopeproperty is not set and therefore a
percentage is not displayed in the browser. We can lessen the impact of this by setting an initial value.

Second, we have returned calculateCategoryPercentage to its simple value service version. But rather than hard-
coding the available categories, calculateCategoryPercentage is passed as follows:

angular.module('udemyAdmin')

.value('calculateCategoryPercentage', function(articles, availableCategories) {


var uniqueCategories = [];
articles.forEach(function(article) {
article.categories.forEach(function(category) {
if (uniqueCategories.indexOf(category) == -1) {
uniqueCategories.push(category);
}
});
});

return Math.floor(100 * (uniqueCategories.length / availableCategories.length));


});

$httpBackend

For the purposes of a tutorial, it is desirable to avoid introducing any tangential complexity. In our case, that would be a
server to respond to the URL ‘/categories’.

AngularJS has a very useful additional module for backend-less development, namely $httpBackend. This is perfect for
tutorials, but I have also used it in a team of four AngularJS developers building an application against a partially built
server. We didn’t want the road map of server features to impact us, so for a period of time, we developed backend-less.

Download angular-mocks.js and add another script reference in index.html in the following order:

<script src="angular.js"></script>
<script src="angular-mocks.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
<script src="services.js"></script>

There are two variants of $httpBackend, one for unit testing and one for backend-less development. We need to tell our
application to use the backend-less variant; this is achieved by adding a dependency to ngMockE2E to
our udemyAdmin module. We then to configure how $httpBackend should respond to URL and HTTP verb
combinations. Currently we only need one combination, HTTP GET and ‘/categories’.

Edit app.js as follows:

angular.module('udemyAdmin', ['ngMockE2E']).run(function($httpBackend) {
$httpBackend.whenGET('/categories').respond(['tutorial', 'hardware', 'graphics']);
});

In the above snippet, the run method of our ‘udemyAdmin’ module is used for one-off configuration code that needs to
execute as our application starts up.

Promises

We first looked at fetching categories from a server by using $http directly in articleCtrl. For this to work, we would
have to undo some of the decoupling we previously achieved by having calculateCategoryPercentage as a factory
service with a dependency on availableCategories.

Let’s now return to that and see if we can extract $http from articleCtrl and use it in availableCategories. If we
can achieve that, articleCtrl and calculateCategoryPercentagecan become oblivious to how the available
categories are obtained. This is a more advanced technique that will help keep code flexible as our applications grow.

We glossed over it in our simple illustration of $http, but the get method made the HTTP request (which is inherently
asynchronous) and immediately returned a Promise object. A Promise object represents the future value of an
asynchronous operation. If the operation succeeds, then the Promise is ‘resolved’; if it fails, then the Promise is ‘rejected’.
A Promise object exposes several methods, one of which then allows us to register a resolution and/or rejection handler.
In our simple usage, we just registered a resolution handler as follows:

$http.get('/categories').then(function(response) {
$scope.categoryPercentage = calculateCategoryPercentage($scope.articles,
response.data);
});

Promises can, without doubt, get reasonably complicated. If you want to learn more, I suggest studying the readme for
the Q library. AngularJS implements a lightweight version of the Q library.

Now that we’ve briefly introduced Promises, let’s look at how we can use them to get our decoupling back. In the following
snippets, I’ve added numbered comments, which we’ll discuss in detail shortly.

Edit controllers.js as follows:

angular.module('udemyAdmin').controller('articleCtrl', function($scope,
calculateCategoryPercentage, pageSize, availableCategories) {

$scope.categories = [];

availableCategories.then(function(categories) {
Array.prototype.splice.apply(
$scope.categories,
[$scope.categories.length, 0].concat(categories)
);
});

$scope.articles = [
{ title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
{ title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
{ title: "Django Tutorial", categories: ['tutorial'] }
];

$scope.$watch('articles', function(articles) {
// 3
calculateCategoryPercentage(articles).then(function(percentage) {
// 6
$scope.categoryPercentage = percentage;
});
}, true);

$scope.containsCategory = function(article, category) {


return article.categories.indexOf(category) >= 0;
};

$scope.toggleCategory = function(article, category) {


var index = article.categories.indexOf(category);

if (index == -1) {
article.categories.push(category);
} else {
article.categories.splice(index, 1);
}
};

$scope.newTitle = '';

$scope.addArticle = function() {
$scope.articles.push({ title: $scope.newTitle, categories: [] });
};

$scope.numArticles = pageSize;
});

Edit services.js as follows:

angular.module('udemyAdmin')

.value('pageSize', 1)

.factory('availableCategories', function($http) {
// 1
return $http.get('/categories').then(function(response) {
return response.data;
});
})

.factory('calculateCategoryPercentage', function(availableCategories) {
// 2
return function calculate(articles) {
var uniqueCategories = [];

articles.forEach(function(article) {
article.categories.forEach(function(category) {
if (uniqueCategories.indexOf(category) == -1) {
uniqueCategories.push(category);
}
});
});

// 4
return availableCategories.then(function(categories) {
// 5
return Math.floor(100 * (uniqueCategories.length / categories.length));
});
};
});

In the above snippets, the numbered comments relate to the following points:

1. articleCtrl depends on calculateCategoryPercentage which depends on availableCategories.


The Promise object returned from $http.get('/categpries').then is registered
as availableCategories. Note that availableCategories has been made a factory service so that it, too,
can have a dependency injected (namely, $http).
2. calculateCategoryPercentage is next in the dependency chain, so the function calculate is registered.
3. articleCtrl runs as the last step in the dependency chain. It calls calculateCategoryPercentage each
time its articles change (via $scope.$watch). A Promise object is returned, and articleCtrl assigns a
resolution handler.
4. A resolution handler is assigned to the availableCategories Promise object. Assigning resolution handlers
via then returns another Promise object, which allows for chained resolution.
5. availableCategories is resolved (i.e., a response is received from the server), and the category percentage is
calculated and returned.
6. The chained resolution set in step 4 allows articleCtrl to set the category percentage as a $scope property.

You may wonder about the benefit of this approach over the simpler use of $http direct in articleCtrl we had
previously. In both approaches, we have had to change how calculateCategoryPercentage is used
in articleCtrl. In this approach, the change has been to work with Promises. Promises are a very general API. For
example, in the future our application could first look in the browser’s local storage for categories before resorting to an
HTTP server call. The Promise API that articleCtrl works with wouldn’t change one bit, but behind the scenes,
obtaining the categories would be more involved. With Promises, articleCtrl has no insight into how the categories
are obtained for the calculation, just that somehow they are.

$resource

Until now, the initial articles in our blog admin application have been hard-coded in articleCtrl. This clearly isn’t the
most flexible application around; as such, the requirements have changed yet again.

We’re now asked to provide a means of retrieving and creating articles stored on a server. The server has provided us a
RESTful API for interacting with articles. Sending an HTTP GET request to the URL ‘/articles’ will return an array of
articles, while sending an HTTP POST request will create and return a new article. $resource is another additional
module and is perfect for working with this type of API.

Download angular-resource.js and add another script reference in index.html in the following order:

<script src="angular.js"></script>
<script src="angular-resource.js"></script>
<script src="angular-mocks.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
<script src="services.js"></script>

As with $http previously, the simplest way to get up and running with $resource is to use it directly in articleCtrl.
We could encapsulate $resource in another factory service so that articleCtrl isn’t aware of how articles are
retrieved and created. For our purposes, the first approach allows us to focus on the detail of using $resource, but in a
larger real-world application, I would certainly consider the latter approach.

Edit app.js as follows:

angular.module('udemyAdmin', ['ngResource', 'ngMockE2E']).run(function($httpBackend) {


$httpBackend.whenGET('/categories').respond(['tutorial', 'hardware', 'graphics']);
});

The change in the above snippet is a simple one: we’ve just added an additional dependency for the ‘udemyAdmin’
module, namely ‘ngResource’.

Edit controllers.js as follows:

angular.module('udemyAdmin').controller('articleCtrl', function($scope,
calculateCategoryPercentage, pageSize, availableCategories, $resource) {

var Article = $resource('/articles');

$scope.categories = [];

availableCategories.then(function(categories) {
Array.prototype.splice.apply(
$scope.categories,
[$scope.categories.length, 0].concat(categories)
);
});

$scope.articles = Article.query();

$scope.$watch('articles', function(articles) {
calculateCategoryPercentage(articles).then(function(percentage) {
$scope.categoryPercentage = percentage;
});
}, true);

$scope.containsCategory = function(article, category) {


return article.categories.indexOf(category) >= 0;
};

$scope.toggleCategory = function(article, category) {


var index = article.categories.indexOf(category);

if (index == -1) {
article.categories.push(category);
} else {
article.categories.splice(index, 1);
}
};

$scope.newTitle = '';

$scope.addArticle = function() {
var newArticle = new Article({ title: $scope.newTitle });
newArticle.$save().then(function(article) {
$scope.articles.push(article);
});
};

$scope.numArticles = pageSize;
});

In the above snippet, $resource is declared as a dependency of articleCtrl and subsequently injected in. We create a
resource object Article by invoking $resource with our URL.

We then invoke Article.query to retrieve an array of articles from the server. This results in an HTTP GET request to
‘/articles’. As this is an asynchronous operation, it does not block the application waiting for the server to respond, but
what value would be useful to return in the meantime? $resource immediately returns an empty array, which will be
filled when the server responds. This is a neat trick, as combined with setting the array as a property on $scope, any
binding we make in our HTML will automatically update.

We haven’t looked at the contents of index.html for a while, but the usage of ng-repeatdoesn’t need to change, even
though we’ve switched to $resource and $scope.articles is, at first, an empty array.

Returning to the snippet of articleCtrl above, when $scope.addArticle is invoked we now create a new instance
of Article with $scope.newTitle as its title. We then invoke $saveresulting in an HTTP POST request to ‘/articles’.
A Promise object is returned, which is resolved by the server responding with an HTTP 200 status code and body. A
instance of Article is created from the server response for us, and we simply push it onto $scope.articles. The
usage of $scope.addArticle and $scope.newTitle in index.htmldoes not need to change.

$httpBackend again

As $resource is an extension of $http, we can also use $httpBackend to configure how requests to the URL ‘/articles’
should be handled.

Edit app.js as follows:

angular.module('udemyAdmin', ['ngResource', 'ngMockE2E']).run(function($httpBackend) {


$httpBackend.whenGET('/categories').respond(['tutorial', 'hardware', 'graphics']);
var articles = [
{ title: "Arduino Tutorial", categories: ['tutorial', 'hardware'] },
{ title: "After Effects Tutorial", categories: ['tutorial', 'graphics'] },
{ title: "Django Tutorial", categories: ['tutorial'] }
];

$httpBackend.whenGET('/articles').respond(articles);
$httpBackend.whenPOST('/articles').respond(function(method, url, data) {
var article = angular.fromJson(data);
article.categories = article.categories || [];
articles.push(article);
return [200, article, {}];
});
});

In the above snippet, we’ve simply moved the hard-coded array of articles from articleCtrl and returned it whenever
an HTTP GET request is made to ‘/articles’. An HTTP POST request to ‘/articles’ pushes another article onto the array,
meaning that any subsequent GET requests would include it.

Summary

If you’ve made this far, after all that copy and pasting of snippets, then I salute you for sticking with me.

You should have a working sample application that demonstrates AngularJS as a great framework that’s both powerful
and fun to use.

This tutorial certainly doesn’t cover everything. My hope is that it has covered enough of the basics that, along with a
couple of intermediate concepts, it will give you the knowledge to continue the journey you have started with AngularJS.

You might also like