Of course, a and b could be on different objects, and you could make the list as long as
you like. If the list is long, you would likely write a function that returns the concatenated
value rather than relying on an expression for the logic.
In the second case, you might want to watch all the properties on the things object. In
this case, you could do this:
$scope.$watch('things', callMe(...), true);
Here, passing in true as the third parameter asks Angular to walk the properties of
things and call callMe() on a change to any of them. This works equally well on an
array as it does here on an object.
Organizing Dependencies with Modules
In any non-trivial application, figuring out how to organize the functionality of your
code into areas of responsibility is often a hard task. We’ve seen how controllers give us
a place to put the code that exposes the right data and functions to the view template.
But what about the rest of the code we need to support our applications? The most
obvious place to put this would be in functions on the controllers.
This works fine for small apps and the examples that we’ve seen so far, but it quickly
becomes unmanageable in real apps. The controllers would become a dumping ground
for everything and anything we need to do. They’d be hard to understand and likely
hard to change.
Organizing Dependencies with Modules | 33
www.it-ebooks.info
Enter modules. They provide a way to group dependencies for a functional area within
your application, and a mechanism to automatically resolve dependencies (also known
as dependency injection). Generically, we call these dependencies services, as they pro‐
vide specific services to our application.
For example, if in our shopping website a controller needs to get a list of items for sale
from the server, we’d want some object—let’s call it Items—to take care of getting the
items from the server. The Items object, in turn, needs some way to communicate with
the database on the server over XHR or WebSockets.
Doing this without modules looks something like this:
function ItemsViewController($scope) {
// make request to server

// parse response into Item objects

// set Items array on $scope so the view can display it
...
}
While this would certainly work, it has a number of potential problems.

If some other controller also needs to get Items from the server, we now have to
replicate this code. This makes maintenance a burden, as now if we make schema
or other changes, we have to update that code in several places.

With other factors like server authentication, parsing complexity, and so on, it is
difficult to reason about the boundaries of responsibility for this controller object,
and reading the code is harder.

To unit test this bit of code, we’d either need to actually have a server running, or
monkey patch XMLHttpRequest to return mock data. Having to run the server will
make tests very slow, it’s a pain to set up, and it usually introduces flakiness into
tests. The monkey patching route solves the speed and flakiness problems, but it
means you have to remember to un-patch any patched objects between tests, and
it brings additional complexity and brittleness by forcing you to specify the exact
on-the-wire format for your data (and in having to update the tests whenever this
format changes).
With modules, and the dependency injection we get from them, we can write our con‐
troller much more simply, like this:
function ShoppingController($scope, Items) {
$scope.items = Items.query();
}
34 | Chapter 2: Anatomy of an AngularJS Application
www.it-ebooks.info
You’re probably now asking yourself, “Sure, that looks cool, but where does Items come
from?” The preceding code assumes that we’ve defined Items as a service.
Services are singleton (single-instance) objects that carry out the tasks necessary to
support your application’s functionality. Angular comes with many services like $loca
tion, for interacting with the browser’s location, $route, for switching views based on
location (URL) changes, and $http, for communicating with servers.
You can, and should, create your own services to do all of the tasks unique to your
application. Services can be shared across any controllers that need them. As such,
they’re a good mechanism to use when you need to communicate across controllers and
share state. Angular’s bundled services start with a $, so while you can name them
anything you like, its a good idea to avoid starting them with $ to avoid naming colli‐
sions.
You define services with the module object’s API. There are three functions for creating
generic services, with different levels of complexity and ability:
Function
Defines
provider(name, Object
OR constructor() )
A configurable service with complex creation logic. If you pass an Object, it should have a
function named $get that returns an instance of the service. Otherwise, Angular assumes you’ve
passed a constructor that, when called, creates the instance.
factory(name, $get
Function() )
A non-configurable service with complex creation logic. You specify a function that, when
called, returns the service instance. You could think of this as provider(name, { $get:
$getFunction() } ).
service(name, con
structor() )
A non-configurable service with simple creation logic. Like the constructor option with provider,
Angular calls it to create the service instance.
We’ll look at the configuration option for provider() later, but let’s discuss an example
with factory() for our preceding Items example. We can write the service like this:
// Create a module to support our shopping views
var shoppingModule = angular.module('ShoppingModule', []);
// Set up the service factory to create our Items interface to the
// server-side database
shoppingModule.factory('Items', function() {
var items = {};
items.query = function() {
// In real apps, we'd pull this data from the server...
return [
{title: 'Paint pots', description: 'Pots full of paint', price: 3.95},
{title: 'Polka dots', description: 'Dots with polka, price: 2.95},
{title: 'Pebbles', description: 'Just little rocks', price: 6.95}
];
};
return items;
});
Organizing Dependencies with Modules | 35
www.it-ebooks.info
When Angular creates the ShoppingController, it will pass in $scope and the new
Items service that we’ve just defined. This is done by parameter name matching. That
is, Angular looks at the function signature for our ShoppingController class, and
notices that it is asking for an Items object. Since we’ve defined Items as a service, it
knows where to get it.
The result of looking up these dependencies as strings means that the arguments of
injectable functions like controller constructors are order-independent. So instead of
this:
function ShoppingController($scope, Items) {...}
we can write this:
function ShoppingController(Items, $scope) {...}
and it all still functions as we intended.
To get this to work with our template, we need to tell the ng-app directive the name of
our module, like the following:
<html ng-app='ShoppingModule'>
To complete the example, we could implement the rest of the template as:
<body ng-controller="ShoppingController">
<h1>Shop!</h1>
<table>
<td>{{item.title}}</td>
<td>{{item.description}}</td>
<td>{{item.price | currency}}</td>
</tr>
</table>
</div>
with a resulting app that looks like Figure 2-2.
Figure 2-2. Shop items
How Many Modules Do I Need?
As services themselves can have dependencies, the Module API lets you define depen‐
dencies for your dependencies.
36 | Chapter 2: Anatomy of an AngularJS Application
www.it-ebooks.info
In most applications, it will work well enough to create a single module for all the code
you create and put all of your dependencies in it. If you use services or directives from
third-party libraries, they’ll come with their own modules. As your app depends on
them, you’d refer to them as dependencies of your application’s module.
For instance, if you include the (fictitious) modules SnazzyUIWidgets and SuperData‐
Sync, your application’s module declaration would look like this:
var appMod = angular.module('app', ['SnazzyUIWidgets', 'SuperDataSync'];
Formatting Data with Filters
Filters allow you to declare how to transform data for display to the user within an
interpolation in your template. The syntax for using filters is:
{{ expression | filterName : parameter1 : ...parameterN }}
where expression is any Angular expression, filterName is the name of the filter you
want to use, and the parameters to the filter are separated by colons. The parameters
themselves can be any valid Angular expression.
Angular comes with several filters, like currency, which we’ve seen:
{{12.9 | currency}}
This bit of code will display the following:
$12.90
We put this declaration in the view (rather than in the controller or model) because the
dollar sign in front of the number is only important to humans, and not to the logic we
use to process the number.
Other filters that come with Angular include date, number, uppercase, and more.
Filters can also be chained with additional pipe symbols in the binding. For example,
we can format the previous example for no digits after the decimal by adding the number
filter, which takes the number of decimals to round to as a parameter. So:
{{12.9 | currency | number:0 }}
displays:
$13
You’re not limited to the bundled filters, and it is simple to write your own. If we wanted
to create a filter that title-cased strings for our headings, for example, we could do so as
follows:
var homeModule = angular.module('HomeModule', []);
homeModule.filter('titleCase', function() {
var titleCaseFilter = function(input) {
Formatting Data with Filters | 37
www.it-ebooks.info
var words = input.split(' ');
for (var i = 0; i < words.length; i++) {
words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1);
}
return words.join(' ');
};
return titleCaseFilter;
});
With a template like this:
<body ng-app='HomeModule' ng-controller="HomeController">
<h1>{{pageHeading | titleCase}}</h1>
</body>
and inserting the pageHeading as a model variable via a controller:
function HomeController($scope) {
$scope.pageHeading = 'behold the majesty of your page title';
}
we would see something resembling Figure 2-3.
Figure 2-3. Title case filter
Changing Views with Routes and $location
Though AJAX apps are technically single-page apps (in the sense that they only load an
HTML page on the first request, and then just update areas within the DOM thereafter),
we usually have multiple sub-page views that we show or hide from the user, as appro‐
priate.
We can use Angular’s $route service to manage this scenario for us. Routes let you
specify that, for a given URL that the browser points to, Angular should load and display
a template, and instantiate a controller to provide context for the template.
You create routes in your application by calling functions on the $routeProvider service
as a configuration block. It goes something like this pseudo-code:
var someModule = angular.module('someModule', [...module dependencies...])
someModule.config(function($routeProvider) {
$routeProvider.
when('url', {controller:aController, templateUrl:'/path/to/tempate'}).
when(...other mappings for your app...).

otherwise(...what to do if nothing else matches...);
)};
38 | Chapter 2: Anatomy of an AngularJS Application
www.it-ebooks.info
The preceding code says that when the browser’s URL changes to the specified URL,
Angular will load the template in /path/to/template, and associate the root element of
this template with aController (as if we’d typed ng-controller=aController).
The otherwise() call in the last line tells the route where to go if nothing else matches.
Let’s put it to use. We’re building an email app that will easily win out over Gmail,
Hotmail, and all the others. We’ll call it…A-Mail. For now, let’s start simply. We’ll have
a first view that displays a list of email messages with a date, title, and the sender. When
you click a message, it should show you a new view with the body of that message.
Due to browser security restrictions, if you’re trying the code out your‐
self, you’ll need to serve it from a web server instead of just file://. If you
have python installed, you could serve it by executing python -m Sim
pleHTTPServer 8888 from your working directory.
For the main template, we’ll do something a bit different. Instead of putting everything
in the first page loaded, we’ll just create a layout template that we’ll put our views into.
We’ll place everything that persists from view to view, like our menus, here. In this case,
we’ll just display a heading with the name of our app. We’ll then use the ng-view directive
to tell Angular where we want our views to appear.
index.html
<html ng-app="AMail">
<head>
<script src="src/angular.js"></script>
<script src="src/controllers.js"></script>
</head>
<body>
<h1>A-Mail</h1>
<div ng-view></div>
</body>
</html>
As our view templates will be inserted into the shell we just created, we can write them
as partial HTML documents. For the email list, we’ll use ng-repeat to iterate through
a list of messages and render them into a table.
list.html
<table>
<tr>
<td><strong>Sender</strong></td>
<td><strong>Subject</strong></td>
<td><strong>Date</strong></td>
Changing Views with Routes and $location | 39
www.it-ebooks.info
</tr>
<tr ng-repeat='message in messages'>
<td>{{message.sender}}</td>
<td><a href='#/view/{{message.id}}'>{{message.subject}}</td>
<td>{{message.date}}</td>
</tr>
</table>
Notice here that we’re going to let the user navigate to a particular message by clicking
on the subject. We’ve data bound the URL to message.id, so clicking on a message with
id=1 will take the user to /#/view/1. We’ll use this navigation-by-url, also known as deep-
linking, in the message detail view’s controller, to make a particular message available
to the detail view.
To create this message detail view, we’ll create a template that displays properties from
a single message object.
detail.html
<div><strong>Subject:</strong> {{message.subject}}</div>
<div><strong>Sender:</strong> {{message.sender}}</div>
<div><strong>Date:</strong> {{message.date}}</div>
<div>
<strong>To:</strong>
<span ng-repeat='recipient in message.recipients'>{{recipient}} </span>
<div>{{message.message}}</div>
<a href='#/'>Back to message list</a>
Now, to associate these templates with some controllers, we’ll configure the $routePro
vider with the URLs that invoke our controllers and templates.
controllers.js
// Create a module for our core AMail services
var aMailServices = angular.module('AMail', []);
// Set up our mappings between URLs, templates, and controllers
function emailRouteConfig($routeProvider) {
$routeProvider.
when('/', {
controller: ListController,
templateUrl: 'list.html'
}).
// Notice that for the detail view, we specify a parameterized URL component
// by placing a colon in front of the id
when('/view/:id', {
controller: DetailController,
templateUrl: 'detail.html'
}).
otherwise({
redirectTo: '/'
40 | Chapter 2: Anatomy of an AngularJS Application
www.it-ebooks.info
});
}
// Set up our route so the AMail service can find it
aMailServices.config(emailRouteConfig);
// Some fake emails
messages = [{
id: 0, sender: 'jean@somecompany.com', subject: 'Hi there, old friend',
date: 'Dec 7, 2013 12:32:00', recipients: ['greg@somecompany.com'],
message: 'Hey, we should get together for lunch sometime and catch up.'
+'There are many things we should collaborate on this year.'
}, {
id: 1, sender: 'maria@somecompany.com',
subject: 'Where did you leave my laptop?',
date: 'Dec 7, 2013 8:15:12', recipients: ['greg@somecompany.com'],
message: 'I thought you were going to put it in my desk drawer.'
+'But it does not seem to be there.'
}, {
id: 2, sender: 'bill@somecompany.com', subject: 'Lost python',
date: 'Dec 6, 2013 20:35:02', recipients: ['greg@somecompany.com'],
message: "Nobody panic, but my pet python is missing from her cage.'
+'She doesn't move too fast, so just call me if you see her."
}, ];
// Publish our messages for the list template
function ListController($scope) {
$scope.messages = messages;
}
// Get the message id from the route (parsed from the URL) and use it to
// find the right message object.
function DetailController($scope, $routeParams) {
$scope.message = messages[$routeParams.id];
}
We’ve created the basic structure for an app with many views. We switch views by
changing the URL. This means that the forward and back buttons just work for users.
Users are able to bookmark and email links to views within the app, even though there
is only one real HTML page.
Talking to Servers
Okay, enough messing around. Real apps generally talk to real servers. Mobile apps and
the emerging Chrome desktop apps may be exceptions, but for everything else, whether
you want persistence in the cloud or real-time interactions with other users, you prob‐
ably want your app to talk to a server.
For this, Angular provides a service called $http. It has an extensive list of abstractions
that make it easier to talk to servers. It supports vanilla HTTP, JSONP, and CORS. It
Talking to Servers | 41
www.it-ebooks.info
includes security provisions to protect from both JSON vulnerabilities and XSRF. It lets
you easily transform the request and response data, and it even implements simple
caching.
Let’s say we want to retrieve products for our shopping site from a server instead of from
our silly in-memory mocks. Writing the server bits is beyond the scope of this book, so
let’s just imagine that we’ve created a service that will return a list of products as JSON
when you make a query to /products.
Given a response that looks like this:
[
{
"id": 0,
"title": "Paint pots",
"description": "Pots full of paint",
"price": 3.95
},
{
"id": 1,
"title": "Polka dots",
"description": "Dots with that polka groove",
"price": 12.95
},
{
"id": 2,
"title": "Pebbles",
"description": "Just little rocks, really",
"price": 6.95
}
...etc...
]
we could write the query like so:
function ShoppingController($scope, $http) {
$http.get('/products').success(function(data, status, headers, config) {
$scope.items = data;
});
}
and use it in a template like this:
<body ng-controller="ShoppingController">
<h1>Shop!</h1>
<table>
<tr ng-repeat="item in items">
<td>{{item.title}}</td>
<td>{{item.description}}</td>
<td>{{item.price | currency}}</td>
</tr>
</table>
42 | Chapter 2: Anatomy of an AngularJS Application
www.it-ebooks.info
</div>
</body>
As we learned previously, we would be better off in the long run by delegating the work
of talking to the server to a service that could be shared across controllers. We’ll take a
look at this structure and the full range of $http functions in Chapter 5.
Changing the DOM with Directives
Directives extend HTML syntax, and are the way to associate behavior and DOM trans‐
formations with custom elements and attributes. Through them, you can create reusable
UI components, configure your application, and do almost anything else you can imag‐
ine wanting to do in your UI template.
You can write apps with the built-in directives that come with Angular, but you’ll likely
run into situations where you want to write your own. You’ll know it’s time to break
into directives when you want to deal with browser events or modify the DOM in a way
that isn’t already supported by the built-in directives. This code of yours belongs in a
directive that you write, and not in a controller, service, or any other place in your app.
As with services, you define directives through the module object’s API by calling its
directive() function, where directiveFunction is a factory function that defines
your directive’s features.
var appModule = angular.module('appModule', [...]);
appModule.directive('directiveName', directiveFunction);
Writing the directive factory function is a deep area, and we’ve dedicated an entire
chapter to it in this book. To whet your appetite, though, let’s look at a simple example.
HTML5 has a great new attribute called autofocus that will place keyboard focus on an
input element. You’d use it to let the user start interacting with the element via his
keyboard without having to click on it first. This is great, as it lets you declaratively
specify what you want the browser to do without having to write any JavaScript. But
what if you wanted to place focus on some non-input element, like a link or any div?
And what if you wanted it to work on non-HTML5 browsers as well? We could do it
with a directive.
var appModule = angular.module('app', []);
appModule.directive('ngbkFocus', function() {
return {
link: function(scope, element, attrs, controller) {
element[0].focus();
}
};
});
Changing the DOM with Directives | 43
www.it-ebooks.info
Here, we’re returning the directive configuration object with its link function specified.
The link function gets a reference to the enclosing scope, the DOM element it lives on,
an array of any attributes passed to the directive, and the controller on the DOM element,
if it exists. Here, we only need to get at the element and call its focus() method.
We can then use it in an example like so:
index.html
<html lang='en' ng-app='app'>
...include angular and other scripts...
<body ng-controller="SomeController">
<button ng-click="clickUnfocused()">
Not focused
</button>
<button ngbk-focus ng-click="clickFocused()">
I'm very focused!
</button>
<div>{{message.text}}</div>
</body>
</html>
controllers.js
function SomeController($scope) {
$scope.message = { text: 'nothing clicked yet' };
$scope.clickUnfocused = function() {
$scope.message.text = 'unfocused button clicked';
};
$scope.clickFocused = function {
$scope.message.text = 'focus button clicked';
}
}
var appModule = angular.module('app', ['directives']);
When the page loads, the user will see the button labeled “I’m very focused!” with the
focus highlight. Hitting the spacebar or the enter key will cause a click and invoke the
ng-click, which will set the div text to ‘focus button clicked’. Opening this example in
a browser, we’d see something that looks like Figure 2-4.
Figure 2-4. Focus directive
44 | Chapter 2: Anatomy of an AngularJS Application
www.it-ebooks.info
Validating User Input
Angular automatically augments <form> elements with several nice features suitable for
single-page applications. One of these nice features is that Angular lets you declare valid
states for inputs within the form and allow submission only when the entire set of
elements is valid.
For example, if we’re creating a signup form where we require entering a name and
email, but have an optional age field, we can validate several user entries before they are
submitted to the server. Loading the example that follows into a browser will display
what is shown in Figure 2-5.
Figure 2-5. Form validation
We’d want to make sure the user had entered text in the name fields, that he had entered
a properly formed email address, and that if he entered an age, it was valid.
We can do this all in the template, using Angular’s extensions to <form> and the various
input elements, like this:
<h1>Sign Up</h1>
<form name='addUserForm'>
<div>First name: <input ng-model='user.first' required></div>
<div>Last name: <input ng-model='user.last' required></div>
<div>Email: <input type='email' ng-model='user.email' required></div>
<div>Age: <input type='number'
ng-model='user.age'
ng-maxlength='3'
ng-minlength='1'></div>
<div><button>Submit</button></div>
</form>
Notice that we’re using the required attribute and input types for email and number
from HTML5 to do our validation on some of the fields. This works great with Angular,
and in older non-HTML5 browsers, Angular will polyfill these with directives that per‐
form the same jobs.
We can then add a controller to this to handle the submission by changing the form to
reference it.
Validating User Input | 45
www.it-ebooks.info
<form name='addUserForm' ng-controller="AddUserController">
Inside the controller, we can access the validation state of the form through a property
called $valid. Angular will set this to true when all the inputs in the form are valid. We
can use this $valid property to do nifty things such as disabling the Submit button
when the form isn’t completed yet.
We can prevent form submission in an invalid state by adding ng-disabled to the
Submit button:
<button ng-disabled='!addUserForm.$valid'>Submit</button>
Finally, we might want the controller to tell the user she’s been successfully added. Our
final template would look like:
<h1>Sign Up</h1>
<form name='addUserForm' ng-controller="AddUserController">
<div ng-show='message'>{{message}}</div>
<div>First name: <input name='firstName' ng-model='user.first' required></div>
<div>Last name: <input ng-model='user.last' required></div>
<div>Email: <input type='email' ng-model='user.email' required></div>
<div>Age: <input type='number'
ng-model='user.age'
ng-maxlength='3'
ng-min='1'></div>
<div><button ng-click='addUser()'
ng-disabled='!addUserForm.$valid'>Submit</button>
</ng-form>
with controller:
function AddUserController($scope) {
$scope.message = '';
$scope.addUser = function () {
// TODO for the reader: actually save user to database...
$scope.message = 'Thanks, ' + $scope.user.first + ', we added you!';
};
}
Moving On
In the last two chapters, we looked at all the most commonly used features in the Angular
framework. For each feature discussed, there are many additional details we have yet to
cover. In the next chapter, we’ll get you going by examining a typical development
workflow.
46 | Chapter 2: Anatomy of an AngularJS Application
www.it-ebooks.info
CHAPTER 3
Developing in AngularJS
By now we have delved a little bit into the cogs that make up AngularJS. We now know
how to get data from the user into our application, how to display text, and how to do
some funky stuff with validation, filtering, and even changing the DOM. But how do
we put it all together?
In this chapter, we will cover:

How to lay out your AngularJS app for rapid development

Starting your server to see your AngularJS app in action

Writing and running your unit and scenario tests using Karma

Compiling and minifying your AngularJS app for production deployment

Debugging your AngularJS app using Batarang

Simplifying your development workflow (from creating new files to running your
application and tests)

Integrating your AngularJS project with RequireJS, a dependency management li‐
brary
This chapter aims to give you a 20,000-foot view of how to possibly lay out your An‐
gularJS app. We won’t go into the actual app itself. That is for Chapter 4, which dives
into a sample application that uses and shows off various AngularJS features.
Project Organization
We recommend seeding your project using Yeoman, which will create all the necessary
files to bootstrap your AngularJS application.
Yeoman is a robust tool comprised of multiple frameworks and client-side libraries. It
provides a rapid development environment by automating some routine tasks needed
47
www.it-ebooks.info
to bootstrap and develop your application. We’ll go through a whole section on how to
install and use Yeoman this chapter, but until then, we will briefly touch upon Yeoman
commands as alternatives to manually performing those operations.
We will also detail the various pieces involved in case you decide not to use Yeoman
because Yeoman does have some issues on Windows computers, and getting it set up
can be slightly challenging.
For those not using Yeoman, we will take a look at a sample application structure (which
can be found in the chapter3/sample-app folder in our GitHub examples repository),
which follows the recommended structure, as well as the structure generated by Yeoman.
The files in the application can be broken into the following categories:
JS source files
Take a look at the app/scripts folder. This is where all your JS source code lives. One
main file (app/scripts/app.js) will set up the the Angular module and the routes for
your application.
In addition, there is a separate folder—app/scripts/controller—which houses the
individual controllers. Controllers provide the action and publish data to the scope
which will then be displayed in the view. Usually, they correspond one to one with
the view.
Directives, filters, and services can also be found under app/scripts, either as com‐
plete files (directives.js, filters.js, services.js), or individually, if they are nice and
complex.
HTML Angular template files
Now, every AngularJS partial template that Yeoman creates can be found in the
app/views folder. This will mirror our app/scripts/controller folder for the most part.
There is one other important Angular template file, which is the main app/
index.html. This is responsible for sourcing the AngularJS source files, as well as
any source files you create for your application.
If you end up creating a new JS file, ensure that you add it to the index.html, and
also update the main module and the routes (Yeoman does this for you as well!).
JS library dependencies
Yeoman provides you the app/scripts/vendor folder for all JS source dependencies.
Want to use Underscore or SocketIO in your application? No problem—add the
dependency to the vendor folder (and your index.html!) and start referencing it in
your application.
Static resources
You are creating an HTML application in the end, and it is a given that you will
have CSS and image dependencies that you need served as part of your application.
48 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
The app/styles and app/img folders are for this very purpose. Just add what you need
and start referring to them (with the correct relative paths, of course!) in your
application.
Yeoman does not create the app/img path by default.
Unit tests
Testing is super important, and totally effortless when it comes to AngularJS. The
test/spec folder should mirror your app/scripts in terms of tests. Each file should
have a mirror spec file which has its unit tests. The seed creates a stub for each
controller file, under test/spec/controllers, with the same name as the original con‐
troller. These are Jasmine-style specs, which describe a specification for each ex‐
pected behavior of the controller.
Integration tests
AngularJS comes with end-to-end testing support built right into the library. All
your E2E tests, in the form of Jasmine specs, are saved under the folder tests/e2e.
Yeoman does not create the tests/folder by default.
While the E2E tests might look like Jasmine, they are not. They are
functions that are executed asynchronously, in the future, by the
Angular Scenario Runner. So don’t expect to be able to do stuff like
you would in a normal Jasmine test (like console.log on the value
of a repeater).
There is also a simple HTML file generated that can be opened by itself in a browser
to run the tests manually. Yeoman doesn’t generate the stubs for these yet, but they
follow a similar style to the unit tests.
Configuration files
There are two configuration files needed. The first one, karma.conf.js, is generated
by Yeoman for you and is used to run the unit tests. The second one, which Yeoman
does not generate yet, is the karma.e2e.conf.js. This is used to run the scenario tests.
There is a sample file at the end of this chapter in the RequireJS integration section.
The config details the dependencies and the files to use when running the unit tests
using Karma. By default, it runs the Karma server at port 9876.
You might ask: how do I run my application? What about unit tests? How do I even
write these various pieces that you are talking about?
Project Organization | 49
www.it-ebooks.info
Don’t worry, young grasshopper, all in due time. In this chapter, we will deal with setting
up your project and development environment so that things can move along at a rapid
pace once we do start churning out some awesome code. What code you write, and how
it hooks together to form your final awesome application, will come in the next few
chapters.
Tools
AngularJS is just one part of your toolkit that allows you to actually develop your web
pages. In this section, we will take a look at various tools that you would use to ensure
efficient and fast development, from IDEs to test runners to debuggers.
IDEs
Let’s start with how you actually edit your source code. There is a whole slew of JavaScript
editors out there, both free and paid. Things have come a long way from the days when
Emacs or Vi was the best option to develop in JS. Nowadays, IDEs come with syntax
highlighting, auto-completion, and so much more, and it might be worth your while to
give one a whirl. So which one should you use?
WebStorm. If you don’t mind shelling out a few bucks (though there is a free 30-day
trial!), then WebStorm by JetBrains offers one of the most comprehensive web devel‐
opment platforms in recent times. It has features that were only previously available for
typed languages, including code-completion (browser specific at that, as shown in
Figure 3-1), code navigation, syntax, error highlighting, and out-of-the-box support for
multiple libraries and frameworks. In addition, there is some very nice integration for
debugging JavaScript right from the IDE while it is executing in Chrome.
Figure 3-1. Browser specific code completion in WebStorm
The biggest reason you should seriously consider WebStorm for AngularJS develop‐
ment is that it is one of the only IDEs that has an AngularJS plug-in. The plug-in gives
you auto-complete support for AngularJS HTML tags right in your HTML templates.
In addition, one of the coolest things it supports is the concept of live templates. These
are pre-formed templates for common code snippets that you would otherwise type
from scratch every time. So instead of typing the following:
directive('$directiveName$', function factory($injectables$) {
var directiveDefinitionObject = {
50 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
$directiveAttrs$
compile: function compile(tElement, tAttrs, transclude) {
$END$
return function (scope, element, attrs) {
}
}
};
return directiveDefinitionObject;
});
in WebStorm, you can just type:
ngdc
and press the tab key to get the same thing. This is just one of the many code-completions
the plug-in provides.
Running Your Application
Now let’s talk about how we get to the payload of all that we do—seeing your application
live, in the browser. To really get a feel for how the application would work, we need to
have a web server serving our HTML and JavaScript code. I will explore two ways: one
very simple way of running your application with Yeoman, and another not so easy, but
just as good, method without Yeoman.
With Yeoman
Yeoman makes it simple for you to start a web server and serve all your static and
AngularJS-related files. Just execute the following command:
yeoman server
and it will start up a server and open your browser with the main page of your AngularJS
application. It will even refresh the browser whenever you make changes to your source
code. How cool is that?
Without Yeoman
Without Yeoman, you would need to configure a web server to serve all the files in your
main directory. If you don’t know an easy way to do that, or don’t want to waste time
creating your own web server, you can quickly write a simple web server using ExpressJS
(as simple as npm install -g express to get it) in Node. It might look something like
the following:
// available at chapter3/sample-app/web-server.js
var express = require("express"),
app = express(),
port = parseInt(process.env.PORT, 10) || 8080;
Running Your Application | 51
www.it-ebooks.info
app.configure(function(){
app.use(express.methodOverride());
app.use(express.bodyParser());
app.use(express.static(__dirname + '/'));
app.use(app.router);
});
app.listen(port);
console.log('Now serving the app at http://localhost:' + port + '/app');
Once you have the file, you can run the file using Node, by executing the following
command:
node web-server.js
and it will start up the server on port 8080 (or one of your own choosing).
Alternatively, with Python in the folder with your application you could run:
python -m SimpleHTTPServer
Whichever way you decide to proceed, once you have the server configured, up and
running, navigate to the following:
http://localhost:[port-number]/app/index.html
in your browser to see the application you have just created. Do note that you will have
to manually refresh your browser to see the changes, unlike with Yeoman.
Testing with AngularJS
We have said it before (even right in this chapter), and we will say it again: testing is
essential, and AngularJS makes it simple to write the right kind of unit and integration
tests. While AngularJS plays nicely with multiple test runners, we strongly believe that
Karma trumps most of them providing the most robust, solid, and insanely fast test
runner for all your needs.
Karma
Karma’s main reason for existence is to make your test-driven development (TDD)
workflow simple, fast, and fun. It uses NodeJS and SocketIO (you don’t need to know
what they are, just assume that they are awesome, cool libraries) to allow running your
52 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
code, and tests in multiple browsers at insanely fast speeds. Go find out more at https://
github.com/vojtajina/karma/.
TDD: An Intro
Test-driven development, or TDD, is an AGILE methodology that flips the development
lifecycle by ensuring that tests are written first, before the code is implemented, and that
tests drive the development (and are not just used as a validation tool).
The tenets of TDD are simple:

Code is written only when there is a failing test that requires the code to pass

The bare minimum amount of code is written to ensure that the test passes

Duplication is removed at every step

Once all tests are passing, the next failing test is added for the next required func‐
tionality.
These simple rules ensure that:

Your code develops organically, and that every line of code written is purposeful.

Your code remains highly modular, cohesive, and reusable (as you need to be able
to test it).

You provide a comprehensive array of tests to prevent future breakages and bugs.

The tests also act as specification, and thus documentation, for future needs and
changes.
We at AngularJS have found this to be true, and the entire AngularJS codebase has been
developed using TDD. For an uncompiled, dynamic language like JavaScript, we strong‐
ly believe that having a good set of unit tests will reduce headaches in the future!
So how do we get this awesomeness that is Karma? Well, first ensure that NodeJS is
installed on your machine. This comes with NPM (Node Package Manager), which
makes it easy to manage and install the thousands of libraries available for NodeJS.
Once you have NodeJS and NPM installed, installing Karma is as easy as running:
sudo npm install -g karma
There you go. You are ready to start Karmaing (I just made that up, please don’t go about
using it in real life) in three easy steps!
Testing with AngularJS | 53
www.it-ebooks.info
Getting your config file up
If you used Yeoman to create your app skeleton, then you already have a ready-
made Karma config file waiting for you to use. If not, just go ahead and execute the
following command from the base folder of your application directory:
karma init
in your terminal console, and it will generate a dummy config file (karma.conf.js)
for you to edit to your liking, with some pretty standard defaults. You can use that.
Starting the Karma server
Just run the following command:
karma start [optionalPathToConfigFile]
This will start the Karma server on port 9876 (the default, which you can change
by editing the karma.conf.js file from the previous step). While Karma should open
up a browser and capture it automatically, it will print all the instructions needed
to capture another browser in the console. If you are too lazy to do that, just go to
http://localhost:9876 in another browser or device, and you are good to start run‐
ning tests in multiple browsers.
While Karma can capture the usual browsers automatically, on start
(Firefox, Chrome, IE, Opera, and even PhantomJS), it is not limited to
just those browsers. Any device on which you can browse to a URL can
possibly be a runner for Karma. So if you open up the browser of your
iPhone or Android device and browse to http://machinename:9876
(provided it is accessible!), you could potentially run your tests on mo‐
bile devices as well.
Running the tests
Execute the following command:
karma run
That’s it. You should get your results printed right in the console where you ran the
command. Easy, isn’t it?
Unit Tests
AngularJS makes it easy to write your unit tests, and supports the Jasmine style of writing
tests by default (as does Karma). Jasmine is what we call a behavior-driven development
framework, which allows you to write specifications that denote how your code should
behave. A sample test in Jasmine might look something like this.
54 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
describe("MyController:", function() {
it("to work correctly", function() {
var a = 12;
var b = a;
expect(a).toBe(b);
expect(a).not.toBe(null);
});
});
As you can see, it lends itself to a very readable format, as most of the code that could
be read in plain English. It also provides a very diverse and powerful set of matchers
(like the expect clauses), and of course has the xUnit staples of setUp and tearDowns
(functions that are executed before and after each individual test case).
AngularJS provides some nice mockups, as well as testing functions, to allow you to
create services, controllers, and filters right in your unit tests, as well as mock out
HttpRequests and the like. We will cover this in Chapter 5.
Karma can be integrated with your development workflow to make it easier, as well as
to get faster feedback on the code you have written.
Integration with IDEs
Karma does not have plug-ins (yet!) for all the latest and greatest IDEs, but you
don’t really need any. All you need to do is add a shortcut command to execute
“karma start” and “karma run” from within your IDE. This can usually be done by
adding a simple script to execute, or the actual shell command, depending on your
choice of editor. You should see the results every time it finishes running, of course.
Running tests on every change
This is utopia for many TDD developers: being able to run all their tests, every time
they press save, within a few milliseconds, and get results back quickly. And this
can be done with AngularJS + Karma pretty easily. Turns out, the Karma config file
(remember the karma.conf.js file from before?) has an innocuous-looking flag
named “autoWatch”. Setting it to true tells Karma to run your tests every time the
file it watches (which is your source and test code) changes. And if you do “karma
start” from within your IDE, guess what? The results from the Karma run will be
available right within your IDE. You won’t even need to switch to console or terminal
to figure out what broke!
End-to-End/Integration Tests
As applications grow (and they tend to, really fast, before you even realize it), testing
whether they work as intended manually just doesn’t cut it anymore. After all, every
time you add a new feature, you have to not only verify that the new feature works, but
also that your old features still work, and that there are no bugs or regressions. If you
End-to-End/Integration Tests | 55
www.it-ebooks.info
start adding multiple browsers, you can easily see how this can become a combinatorial
explosion!
AngularJS tries to ease that by providing a Scenario Runner that simulates user inter‐
actions with your application.
The Scenario Runner allows you to describe your application in a Jasmine-like syntax.
Just as with the unit tests before, we will have a series of describes (for the feature),
and individual its (to describe each individual functionality of the feature). As always,
you can have some common actions, to be performed before and after each spec (as we
call a test).
A sample test that looks at an application that filters a list of results might look something
like the following:
describe('Search Results', function() {
beforeEach(function() {
browser().navigateTo('http://localhost:8000/app/index.html');
});
it('should filter results', function() {
input('searchBox').enter('jacksparrow');
element(':button').click();
expect(repeater('ul li').count()).toEqual(10);
input('filterText').enter('Bees');
expect(repeater('ul li').count()).toEqual(1);
});
});
There are two ways of running these tests. Either way you run them, though, you must
have a web server started that serves your application (refer to previous section for more
information on how to do that). Once that is done, use one of the following methods:
1.
Automated: Karma now supports running of Angular scenario tests. Create a Kar‐
ma config file with the following changes:
a.
Add ANGULAR_SCENARIO & ANGULAR_SCENARIO_ADAPTER to the
files section of the config.
b.
Add a proxies section that redirects requests to the server to the correct folder
where your test files are located, for example:
proxies = {'/': 'http://localhost:8000/test/e2e/'};
c.
Add a Karma root to ensure that Karma’s source files don’t interfere with your
tests, like so:
urlRoot = '/_karma_/';
Then just remember to capture your Karma server by browsing to http://local‐
host:9876/_karma_, and you should be free to run your tests using Karma.
56 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
2.
Manual: The manual method allows you to open a simple page from your web
server and run (and see) all the tests. To do so, you must:
a.
Create a simple runner.html file, which sources the angular-scenario.js file from
the Angular library.
b.
Source all your JS files which hold the specifications that you have written as
part of your Scenario suite.
c.
Start your web server, and browse to the runner.html file.
Why should you use the Angular Scenario Runner over, say, an external third party
integration or end-to-end test runner? There are some amazing benefits that you get
from using the Scenario Runner, including:
AngularJS aware
The Angular Scenario Runner, as the name suggests, is made by and for Angular.
Thus, it is AngularJS aware, and knows and understands the various AngularJS
elements, like bindings. Need to input some text? Check the value of a binding?
Verify the state of a repeater? All can be done easily through the use of the scenario
runner.
No more random waits
The Angular awareness also means that Angular is aware of all XHRs being made
to the server, and thus can avoid waiting for random intervals of time for pages to
load. The Scenario Runner knows when a page has loaded, and thus is much more
deterministic than a Selenium test, for example, where tests can fail by timing out
while waiting for a page to load.
Debugging capabilities
Wouldn’t it be nice if you could look at your code, dig into the JavaScript, and pause
and resume the test when you wanted to, all while the Scenario tests were running?
With the Angular Scenario Runner, all this is possible, and much more.
Compilation
Compilation in the JavaScript world usually means minification of the code, though
there is some amount of actual compilation possible using the Google Closure Library.
But why would you want to convert all that glorious, well-written, and easily under‐
standable code to almost pure gibberish?
One reason is the goal of making applications that are quick and responsive for the user.
That is a major reason why client-side applications took off like they did a few years ago.
And the sooner you can get your application up and running, the sooner it will be
responsive.
Compilation | 57
www.it-ebooks.info
That responsiveness is the motivation of minification of JS code. The smaller the code,
the smaller the payload, and the faster the transmission of the file to the user’s browser.
This becomes especially important in mobile apps, where size becomes the bottleneck.
There are a few ways you can minify the AngularJS code that you have written for your
app, each with varying levels of effectiveness.
Basic and simple optimization
This involves minifying all the variables that you use in your code, but avoiding
minifying the properties. This is known as the Simple optimization pass in Closure
Compiler.
This will not give you a great reduction in file size, but you’ll still get a substantial
one, for minimal overhead.
The reason this works is that the compiler (Closure or UglifyJS) avoids renaming
properties that are referenced from the templates. Thus, your templates continue
to work, and only local variables and parameters are renamed.
With Google Closure, this is as simple as calling:
java -jar closure_compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS
--js path/to/file.js
Advanced optimization
Advanced optimization is a bit more tricky, as it tries to rename pretty much any
and every function possible. To get this level of optimization to work, you will need
to handhold the compiler a bit by telling it explicitly (through the use of an ex
terns file) which functions, variables, and properties should not be renamed. These
are generally the functions and properties accessed by the templates.
The compiler will use this externs file and then rename everything else. If done
properly, this can result in a substantial reduction in the size of your JavaScript, but
it does require a significant amount of work, including updating the externs file
every time your code changes.
One thing to keep in mind: you have to use the declared form of dependency in‐
jection (specifying the $inject property on the controller) when you want to minify
your code.
This will not work:
function MyController($scope, $resource) {
// Stuff here
}
You will need to do one of the following instead:
function MyController($scope, $resource) {
// Same stuff here
58 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
}
MyController.$inject = [‘$scope’, ‘$resource’];
or use the module, like so:
myAppModule.controller(‘MyController’, [‘$scope’,
‘$resource’,
function($scope, $resource) {
// Same stuff here
}]);
This is the only way AngularJS can figure out which service or variable you were
originally asking for once all the variables are obfuscated or compressed.
It is generally good practice to use the array-style injection all the time,
to avoid bugs later when you start compiling the code. Scratching your
head later and trying to figure out why the provider of the $e variable
(the minified, obfuscated version of some service) is suddenly missing
is just not worth it.
Other Awesome Tools
In this section, we will take a look at some other tools that will help ease your develop‐
ment flow and make you that much more productive. These range from debugging with
Batarang to actual coding and development with Yeoman.
Debugging
When you work with JavaScript, debugging your code in the browser is going to become
second nature. The sooner you accept that, the better off you will be. Thankfully, things
have come a long way since the old days when there was no Firebug. Now, regardless
of the choice of browser, there is generally something you can use to step in to your
code, analyze your errors, and figure out the state of the application. Get to know the
Developer Tools in Chrome and Internet Explorer; Firebug works across Firefox and
Chrome.
A few further tips to help you out when debugging your application:

Always, always switch to the non-minified version of all your source code and
dependencies when you want to debug. Not only will you get better variable names,
you’ll also get line numbers and actual useful information and debugging capabil‐
ities.

Try to keep your source code in individual JS files, not inlined in HTML.
Other Awesome Tools | 59
www.it-ebooks.info

Breakpoints are useful! They allow you to check the state of your application, its
models, and everything in between at a given point in time.

“Pause on all exceptions” is a very useful option that is built in to most developer
tools nowadays. The debugger will halt when an exception occurs, and highlight
the line causing it.
Batarang
And then, of course, we have Batarang. Batarang is a Chrome extension that adds An‐
gularJS knowledge to the built-in Developer Tools suite in Google Chrome. Once in‐
stalled (you can get it from http://bit.ly/batarangjs), it adds another tab to the Developer
Tools panel of Chrome called AngularJS.
Have you ever wondered what the current state of your AngularJS application is? What
each model, each scope, and each variable currently contains? How is the performance
of your application? if you haven’t yet, trust me, you will! And when you do, Batarang
is there for you!
There are four main useful additions in Batarang.
Model tab
Batarang allows you to dig into the scope, from the root downwards. You can then see
how scopes are nested and how models are attached to them (as shown in Figure 3-2).
You can even change them in real time and see the changes reflected in your application.
How cool is that?
60 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
Figure 3-2. Model tree in Batarang
Performance tab
The performance tab must be enabled separately, as it injects some special JavaScript
juice into your application. Once you enable it, you can look at various scopes and
models, and evaluate the performance of all the watch expressions in each scope (as
shown in Figure 3-3). The performance also gets updated as you use the app, so it works
in real time as well!
Other Awesome Tools | 61
www.it-ebooks.info
Figure 3-3. Performance tab in Batarang
Service dependencies
For a simple application, you won’t have more than one or two dependencies for your
controllers and services. But in a real, full-scale application, service dependency man‐
agement can become nightmarish without the proper tool support. Batarang is there
for you, filling this very hole, as it gives you a clean, simple way of visualizing your
service dependency chart (as shown in Figure 3-4).
62 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
Figure 3-4. Charting dependencies in Batarang
Elements properties and console access
When you dig through the HTML template code of an AngularJS application, there is
now an additional AngularJS Properties section in the Properties pane of the Elements
tab. This allows you to inspect the models attached to a given element’s scope. It also
exposes the scope of the element to the console, so that you can access it through the
$scope variable in the console. This is shown in Figure 3-5.
Other Awesome Tools | 63
www.it-ebooks.info
Figure 3-5. AngularJS properties within Batarang
Yeoman: Optimizing Your Workflow
There are quite a few tools that have sprung up to help optimize your workflow when
developing web applications. Yeoman, which we touched upon in previous sections, is
one such tool that boasts an impressive set of features, including:

Lightning-fast scaffolding

Built-in preview server

Integrated package management

An awesome build process

Unit testing using PhantomJS
It also integrates nicely and extensively with AngularJS, which is one of the foremost
reasons why we strongly recommend using it for any AngularJS project. Let’s walk
through the various ways that Yeoman makes your life easier:
64 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
Installing Yeoman
Installing Yeoman is quite an involved process, but there are scripts to help you through
it.
On a Mac/Linux machine, run the following command:
curl -L get.yeoman.io | bash
and just follow the instructions it prints to get Yeoman.
For Windows, or if you run into any issues, go to https://github.com/yeoman/yeoman/
wiki/Manual-Install and follow the instructions there to get you unblocked.
Starting a Fresh AngularJS project
As previously mentioned, even a simple AngularJS project has quite a bit of seeding that
needs to be done, from the templates, the basic controllers, and the library dependencies,
to everything else that needs to be structured. You could do it yourself manually, or use
Yeoman to do it for you.
Simply create a folder for your project (the name of the folder will be taken as the project
name by Yeoman), and then run:
yeoman init angular
This will create the entire structure detailed in the Project Organization part of this
chapter for you, including the skeletons for rendering your routes, your unit tests, and
more.
Running Your Server
If you don’t use Yeoman, you will have to create an HTTP server that serves your front-
end code. But with Yeoman, you get a built-in server that is pre-configured and has
some nice added benefits. You can start the server using:
yeoman server
This not only starts a web server that serves your code, but it also automatically opens
your web browser and refreshes your browser when you make changes to your appli‐
cation.
Adding New Routes, Views, and Controllers
Adding a new route to Angular involves multiple steps, including:

Sourcing the New Controller JS file in the index.html

Adding the correct route to the AngularJS module
Yeoman: Optimizing Your Workflow | 65
www.it-ebooks.info

Creating the template HTML

Adding unit tests
All of this can be accomplished in a single step in Yeoman with the following command:
yeoman init angular:route routeName
So if you ended up running yeoman init angular:route home, it would:

Create a home.js controller skeleton in the app/scripts/controllers folder

Create a home.js test spec skeleton in the test/specs/controllers folder

Add the home.html template to the app/views folder

Hook up the home route in the main app module (app/scripts/app.js file)
All of this from a single command!
The Testing Story
We’ve already seen how ridiculously easy it is to start and run tests using Karma. In the
end, just two commands were needed to run all your unit tests.
Yeoman makes it easier (if you can believe it). Anytime you generate a file using Yeoman,
it also creates a testing stub for you to fill out. Once you’ve installed Karma, running
tests with Yeoman is as simple as executing the following command:
yeoman test
Building Your Project
Building the production-ready version of your app can be a pain, or at least involve
many steps. Yeoman alleviates some of this by allowing you to:

Concatenate all your JS Scripts into one file

Version your files

Optimize images

Generate Application Cache manifests
All these benefits come from just one command:
yeoman build
Yeoman does not support minification yet, but it is coming soon, according to the
developers.
66 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
Integrating AngularJS with RequireJS
Getting your development environment just right is much easier if you get more done
early. Modifying your development environment at a later stage will require modifica‐
tions to a larger number of files. Dependency management and creating deployment
packages are top worries for any sizable project.
With JavaScript, setting up your development environment used to be quite difficult,
as it involved maintaining Ant builds, building scripts to concatenate your files, mini‐
fying them, and more. Thankfully, in the recent past, tools like RequireJS have emerged,
which allow you to define and manage your JS dependencies, as well as hook them into
a simpler build process. With these asynchronous load-management tools, which en‐
sure that all dependencies are loaded before the code is executed, focusing on developing
the actual features has never been easier.
Thankfully, AngularJS can and does play nice with RequireJS, so you can have the best
of both worlds. For the purpose of this example, we will provide a sample setup that we
have found to work nicely, and in a systematic, easy-to-follow way.
Let us take a look at the project organization (similar to the skeletons previously de‐
scribed, with minor changes):
1.
app: This folder hosts all the app code that is displayed to the user. This includes
HTML, JS, CSS, images, and dependent libraries.
a.
/styles: Contains all the CSS/LESS files
b.
/images: Contains images for our project
c.
/scripts: The main AngularJS codebase. This folder also includes our bootstrap‐
ping code, and the main integration with RequireJS
i.
/controllers: AngularJS controllers go here
ii.
/directives: AngularJS Directives go here
iii.
/filters: AngularJS filters go here
iv.
/services: AngularJS services go here
d.
/vendor: The libraries we depend on (Bootstrap, RequireJS, jQuery)
e.
/views: The HTML partials for the views and the components used in our project
2.
config: Contains Karma configs for unit and scenario tests
3.
test: Contains the unit and scenario (integration) tests for the app
a.
/spec: Contains the unit tests, mirroring the structure of the JS folder in the app
directory
b.
/e2e: Contains the end-to-end scenario specs
Integrating AngularJS with RequireJS | 67
www.it-ebooks.info
The first thing we need is the main.js file (in the app folder) that RequireJS loads, which
then triggers loading of all the other dependencies. In this example, our JS project will
depend on jQuery and Twitter Bootstrap in addition to our code.
// the app/scripts/main.js file, which defines our RequireJS config
require.config({
paths: {
angular: 'vendor/angular.min',
jquery: 'vendor/jquery',
domReady: 'vendor/require/domReady',
twitter: 'vendor/bootstrap',
angularResource: 'vendor/angular-resource.min',
},
shim: {
'twitter/js/bootstrap': {
deps: ['jquery/jquery']
},
angular: {
deps: [ 'jquery/jquery',
'twitter/js/bootstrap'],
exports: 'angular'
},
angularResource: { deps:['angular'] }
}
});
require([
'app',
// Note this is not Twitter Bootstrap
// but our AngularJS bootstrap
'bootstrap',
'controllers/mainControllers',
'services/searchServices',
'directives/ngbkFocus'
// Any individual controller, service, directive or filter file
// that you add will need to be pulled in here.
// This will have to be maintained by hand.
],
function (angular, app) {
'use strict';
app.config(['$routeProvider',
function($routeProvider) {
// Define your Routes here
}
]);
}
);
We then define an app.js file. This defines our AngularJS app, and tells it that it depends
on all the controllers, services, filters, and directives we define. We’ll look at the files
that are mentioned in the RequireJS dependency list in just a bit.
68 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
You can think of the RequireJS dependency list as a blocking import statement for
JavaScript. That is, the function within the block will not execute until all the depen‐
dencies listed are satisfied or loaded.
Also notice that we don’t individually tell RequireJS what directive, service, or filter to
pull in, because that is not how this project is structured. There is one module each for
controllers, services, filters, and directives, and thus it is sufficient to just define those
as our dependencies.
// The app/scripts/app.js file, which defines our AngularJS app
define(['angular', 'angularResource', 'controllers/controllers',
'services/services', 'filters/filters',
'directives/directives'], function (angular) {
return angular.module(‘MyApp’, ['ngResource', 'controllers', 'services',
'filters', 'directives']);
});
We also have a bootstrap.js file, which waits for the DOM to be ready (using RequireJS’s
plug-in, domReady), and then tells AngularJS to go forth and be awesome.
// The app/scripts/bootstrap.js file which tells AngularJS
// to go ahead and bootstrap when the DOM is loaded
define(['angular', 'domReady'], function(angular, domReady) {
domReady(function() {
angular.bootstrap(document, [‘MyApp’]);
});
});
There is another advantage to splitting the bootstrap from the app, which is that we
could potentially replace our mainApp with a fake or a mockApp for the purpose of testing.
For example, if the servers you depend on are flaky, you could just create a fakeApp that
replaces all $http requests with fake data to allow you to develop in peace. That way,
you can just slip in a fakeBootstrap and a fakeApp into your application.
Now, your main index.html (which is in the app folder) could look something like:
<!DOCTYPE html>
<html> <!-- Do not add ng-app here as we bootstrap AngularJS manually-->
<head>
<title>My AngularJS App</title>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css"
href="styles/bootstrap.min.css">
<link rel="stylesheet" type="text/css"
href="styles/bootstrap-responsive.min.css">
<link rel="stylesheet" type="text/css" href="styles/app.css">
</head>
<body class="home-page" ng-controller="RootController">
Integrating AngularJS with RequireJS | 69
www.it-ebooks.info
<div ng-view ></div>
<script data-main="scripts/main"
src="lib/require/require.min.js"></script>
</body>
</html>
Now, we’ll take a look at the js/controllers/controllers.js file, which will look almost ex‐
actly the same as js/directives/directives.js, js/filters/filters.js, and js/services/services.js:
define(['angular'], function(angular) {
'use strict';
return angular.module('controllers', []);
});
Because of the way we have our RequireJS dependencies structured, all these are guar‐
anteed to run only after the Angular dependency has been satisfied and loaded.
Each of these files defines an AngularJS module, which will then be used by the indi‐
vidual controllers, directives, filters, and services to add on to the definition.
Let’s take a look at a directive definition (such as our focus directive from Chapter 2):
// File: ngbkFocus.js
define(['directives/directives'], function(directives) {
directives.directive(ngbkFocus, ['$rootScope', function($rootScope) {
return {
restrict: 'A',
scope: true,
link: function(scope, element, attrs) {
element[0].focus();
}
};
}]);
});
The directive itself is quite trivial, but let us take a closer look at what’s happening. The
RequireJS shim around the file says that my ngbkFocus.js depends on the module dec‐
laration file directives/directives.js. It then uses the injected directives module to add on
its own directive declaration. You could choose to have multiple directives, or a single
one per file. It is completely up to you.
One major note: if you have a controller that pulls in a service (say your RootControl
ler depends on your UserService, and gets the UserService injected in), then you have
to make sure that you define the file dependency to RequireJS as well, like so:
define(['controllers/controllers', 'services/userService'],
function(controllers) {
controllers.controller('RootController', ['$scope', 'UserService',
function($scope, UserService) {
// Do what's needed
70 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
};
}]);
});
That is basically how your entire source folder structure is set up.
But how does this affect my tests, you ask? We’re glad you asked that question, because
you are going to get the answer now!
The good news is that Karma does support RequireJS. Just install the latest and greatest
version of Karma (using npm install -g karma).
Once you have done that, the Karma config for the unit tests also changes slightly. The
following is how we would set up the unit tests to run for the project structure we have
previously defined:
// This file is config/karma.conf.js.
// Base path, that will be used to resolve files
// (in this case is the root of the project)
basePath = '../';
// list files/patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
REQUIRE,
REQUIRE_ADAPTER,
// !! Put all libs in RequireJS 'paths' config here (included: false).
// All these files are files that are needed for the tests to run,
// but Karma is being told explicitly to avoid loading them, as they
// will be loaded by RequireJS when the main module is loaded.
{pattern: 'app/scripts/vendor/**/*.js', included: false},
// all the sources, tests // !! all src and test modules (included: false)
{pattern: 'app/scripts/**/*.js', included: false},
{pattern: 'app/scripts/*.js', included: false},
{pattern: 'test/spec/*.js', included: false},
{pattern: 'test/spec/**/*.js', included: false},
// !! test main require module last
'test/spec/main.js'
];
// list of files to exclude
exclude = [];
// test results reporter to use
// possible values: dots || progress
reporter = 'progress';
// web server port
Integrating AngularJS with RequireJS | 71
www.it-ebooks.info
port = 8989;
// cli runner port
runnerPort = 9898;
// enable/disable colors in the output (reporters and logs)
colors = true;
// level of logging
logLevel = LOG_INFO;
// enable/disable watching file and executing tests whenever any file changes
autoWatch = true;
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari
// - PhantomJS
// - IE if you have a windows box
browsers = ['Chrome'];
// Continuous Integration mode
// if true, it captures browsers, runs tests, and exits
singleRun = false;
We use a slightly different format to define our dependencies (the included: false is
quite important). We also add the dependency on REQUIRE_JS and its adapter. The
final thing to get all this working is main.js, which triggers our tests.
// This file is test/spec/main.js
require.config({
// !! Karma serves files from '/base'
// (in this case, it is the root of the project /your-project/app/js)
baseUrl: '/base/app/scripts',
paths: {
angular: 'vendor/angular/angular.min',
jquery: 'vendor/jquery',
domReady: 'vendor/require/domReady',
twitter: 'vendor/bootstrap',
angularMocks: 'vendor/angular-mocks',
angularResource: 'vendor/angular-resource.min',
unitTest: '../../../base/test/spec'
},
// example of using shim, to load non-AMD libraries
// (such as Backbone, jQuery)
shim: {
angular: {
exports: 'angular'
72 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
},
angularResource: { deps:['angular']},
angularMocks: { deps:['angularResource']}
}
});
// Start karma once the dom is ready.
require([
'domReady',
// Each individual test file will have to be added to this list to ensure
// that it gets run. Again, this will have to be maintained manually.
'unitTest/controllers/mainControllersSpec',
'unitTest/directives/ngbkFocusSpec',
'unitTest/services/userServiceSpec'
], function(domReady) {
domReady(function() {
window.__karma__.start();
});
});
So with this setup, we can run the following:
karma start config/karma.conf.js
Then we can run the tests.
Of course there is a slight change when it comes to writing your unit tests. They need
to be RequireJS-supported modules as well, so let’s take a look at a sample test:
// This is test/spec/directives/ngbkFocus.js
define(['angularMocks', 'directives/directives', 'directives/ngbkFocus'],
function() {
describe('ngbkFocus Directive', function() {
beforeEach(module('directives'));
// These will be initialized before each spec (each it(), that is),
// and reused
var elem;
beforeEach(inject(function($rootScope, $compile) {
elem = $compile('<input type=”text” ngbk-focus>')($rootScope);
}));
it('should have focus immediately', function() {
expect(elem.hasClass('focus')).toBeTruthy();
});
});
});
Every test of ours will do the following:
1.
Pull in angularMocks, which gets us angular, angularResource, and of course,
angularMocks.
Integrating AngularJS with RequireJS | 73
www.it-ebooks.info
2.
Pull in the high-level module (directives for directives, controllers for control‐
lers, and so on), then the individual file it is actually testing (the loadingIndicator).
3.
If your test depends on some other service or controller, make sure you also define
the RequireJS dependency, in addition to telling AngularJS about it.
This kind of approach can be used with any test, and you should be good to go.
Thankfully, the RequireJS approach doesn’t affect our end-to-end tests at all, so they can
simply be done the way we have seen so far. A sample config follows, assuming that the
server that runs your app is running on http://localhost:8000.
// base path, that will be used to resolve files
// (in this case is the root of the project
basePath = '../';
// list of files / patterns to load in the browser
files = [
ANGULAR_SCENARIO,
ANGULAR_SCENARIO_ADAPTER,
'test/e2e/*.js'
];
// list of files to exclude
exclude = [];
// test results reporter to use
// possible values: dots || progress
reporter = 'progress';
// web server port
port = 8989;
// cli runner port
runnerPort = 9898;
// enable / disable colors in the output (reporters and logs)
colors = true;
// level of logging
logLevel = LOG_INFO;
// enable / disable watching file and executing tests whenever any file changes
autoWatch = true;
urlRoot = '/_karma_/';
proxies = {
'/': 'http://localhost:8000/'
};
// Start these browsers, currently available:
74 | Chapter 3: Developing in AngularJS
www.it-ebooks.info
browsers = ['Chrome'];
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = false;
Integrating AngularJS with RequireJS | 75
www.it-ebooks.info
www.it-ebooks.info
CHAPTER 4
Analyzing an AngularJS App
We talked about some of the commonly used features of AngularJS in Chapter 2, and
then dived into how your development should be structured in Chapter 3. Rather than
continuing with similarly deep dives into individual features, Chapter 4 will look at a
small, real-life application. We will get a feel for how all the pieces that we have been
talking about (with toy examples) actually come together to form a real, working ap‐
plication.
Rather than putting the entire application front and center, we will introduce one por‐
tion of it at a time, then talk about the interesting and relevant parts, slowly building up
to the entire application by the end of this chapter.
The Application
GutHub is a simple recipe management application, which we designed both to store
our super tasty recipes and to show off various pieces of an AngularJS application. The
application:

has a two-column layout.

has a navigation bar on the left.

allows you to create a new recipe.

allows you to browse the list of existing recipes.
The main view is on the right, which gets changed—depending on the URL—to either
the list of recipes, the details of a single recipe, or an editable form to add to or edit
existing recipes. We can see a screenshot of the application in Figure 4-1.
77
www.it-ebooks.info
Figure 4-1. GutHub: A simple recipe management application
This entire application is available on our GitHub repo in chapter4/guthub.
Relationship Between Model, Controller, and Template
Before we dive into the application, let us spend a paragraph or two talking about how
the three pieces of our application work together, and how to think about each of them.
The model is the truth. Just repeat that sentence a few times. Your entire application is
driven off the model—what views are displayed, what to display in the views, what gets
saved, everything! So spend some extra time thinking about your model, what the at‐
tributes of your object are going to be, and how you are going to retrieve it from the
server and save it. The view will get updated automatically through the use of data
bindings, so the focus should always be on the model.
The controller holds the business logic: how you retrieve your model, what kinds of
operations you perform on it, what kind of information your view needs from the model,
and how you transform the model to get what you want. The responsibility of validation,
making server calls, bootstrapping your view with the right data, and mostly everything
in between belongs on your controller.
Finally, the template represents how your model will be displayed, and how the user will
interact with your application. It should mostly be restricted to the following:

Displaying your model

Defining the ways the user can interact with your application (clicks, input fields,
and so on)
78 | Chapter 4: Analyzing an AngularJS App
www.it-ebooks.info

Styling the app, and figuring out how and when some elements are displayed (show
or hide, hover, and so on)

Filtering and formatting your data (both input and output)
Realize that the template in Angular is not necessarily the view part of the Model View
Controller design paradigm. Instead, the view is the compiled version of the template
that gets executed. It is a combination of the template and the model.
What should not go into the template is any kind of business logic or behavior; this
information should be restricted to the controller. Keeping the template simple allows
a proper separation of concerns, and also ensures that you can get the most code under
test using only unit tests. Templates will have to be tested with scenario tests.
But, you might ask, where does DOM manipulation go? DOM manipulation doesn’t
really go into the controllers or the template. It goes into AngularJS directives (but can
sometimes be used via services, which house DOM manipulation to avoid duplication
of code). We’ll cover an example of that in our GutHub example as well.
Without further ado, let’s dive right in.
The Model
We are going to keep the model dead simple for this application. There are recipes.
They’re about the only model object in this entire application. Everything else builds
off of it.
Each recipe has the following properties:

An ID if it is persisted to our server

A name

A short description

Cooking instructions

Whether it is a featured recipe or not

An array of ingredients, each with an amount, a unit, and a name
That’s it. Dead simple. Everything in the app is based around this simple model. Here’s
a sample recipe for you to devour (the same one referenced in Figure 4-1):
{
"id": "1",
"title": "Cookies",
"description": "Delicious, crisp on the outside, chewy" +
" on the outside, oozing with chocolatey goodness " +
"cookies. The best kind",
"ingredients": [
The Model | 79
www.it-ebooks.info
{
"amount": "1",
"amountUnits": "packet",
"ingredientName": "Chips Ahoy"
}
],
"instructions": "1. Go buy a packet of Chips Ahoy\n" +
"2. Heat it up in an oven\n" +
"3. Enjoy warm cookies\n" +
"4. Learn how to bake cookies from somewhere else"
}
We will go on to see how more complicated UI features can be built around this simple
model.
Controllers, Directives, and Services, Oh My!
Now we finally get to sink our teeth into the meat of this delicious application. First, we
will look at the directives and services code and talk a little bit about what it is doing,
then we’ll take a look at the multiple controllers needed for this application.
Services
// This file is app/scripts/services/services.js
var services = angular.module('guthub.services', ['ngResource']);
services.factory('Recipe', ['$resource',
function($resource) {
return $resource('/recipes/:id', {id: '@id'});
}]);
services.factory('MultiRecipeLoader', ['Recipe', '$q',
function(Recipe, $q) {
return function() {
var delay = $q.defer();
Recipe.query(function(recipes) {
delay.resolve(recipes);
}, function() {
delay.reject('Unable to fetch recipes');
});
return delay.promise;
};
}]);
services.factory('RecipeLoader', ['Recipe', '$route', '$q',
function(Recipe, $route, $q) {
return function() {
var delay = $q.defer();
Recipe.get({id: $route.current.params.recipeId}, function(recipe) {
delay.resolve(recipe);
80 | Chapter 4: Analyzing an AngularJS App
www.it-ebooks.info
}, function() {
delay.reject('Unable to fetch recipe ' + $route.current.params.recipeId);
});
return delay.promise;
};
}]);
Let’s take a look at our services first. We touched upon services in “Organizing Depen‐
dencies with Modules” on page 33. Here, we’ll dig a little bit deeper.
In this file, we instantiate three AngularJS services.
There is a recipe service, which returns what we call an Angular Resource. These are
RESTful resources, which point at a RESTful server. The Angular Resource encapsulates
the lower level $http service, so that you can just deal with objects in your code.
With just that single line of code—return $resource—(and of course, a dependency
on the guthub.services module), we can now put recipe as an argument in any of
our controllers, and it will be injected into the controller. Furthermore, each recipe
object has the following methods built in:

Recipe.get()

Recipe.save()

Recipe.query()

Recipe.remove()

Recipe.delete()
If you are going to use Recipe.delete, and want your application to
work in IE, you will have to call it like so: Recipe[delete](). This is
because delete is a keyword in IE.
Of the the previous methods, all but query work with a single recipe; query() returns
an array of recipes by default.
The line of code that declares the resource—return $resource—also does a few more
nice things for us:
1.
Notice the :id in the URL specified for the RESTful resource. It basically says that
when you make any query (say, Recipe.get()), if you pass in an object with an id
field, then the value of that field will be added to the end of the URL.
That is, calling Recipe.get({id: 15}) will make a call to /recipe/15.
Controllers, Directives, and Services, Oh My! | 81
www.it-ebooks.info
2.
What about that second object? The {id: @id}? Well, as they say, a line of code is
worth a thousand explanations, so let’s take a simple example.
Say we have a recipe object, which has the necessary information already stored
within it, including an id.
Then, we can save it by simply doing the following:
// Assuming existingRecipeObj has all the necessary fields,
// including id (say 13)
var recipe = new Recipe(existingRecipeObj);
recipe.$save();
This will make a POST request to /recipe/13.
The @id tells it to pick the id field from its object and use that as the id parameter.
It’s an added convenience that can save a few lines of code.
There are two other services in apps/scripts/services/services.js. Both of them are Load‐
ers; one loads a single recipe (RecipeLoader), and the other loads all recipes (MultiRe
cipeLoader). These are used when we hook up our routes. At their cores, both of them