Angular JS
Angular JS
Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of O’Reilly
Media, Inc. AngularJS, the image of a thornback cowfish, and related trade dress are trademarks of O’Reilly
Media, Inc.
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as
trademarks. Where those designations appear in this book, and O’Reilly Media, Inc., was aware of a trade‐
mark claim, the designations have been printed in caps or initial caps.
While every precaution has been taken in the preparation of this book, the publisher and authors assume
no responsibility for errors or omissions, or for damages resulting from the use of the information contained
herein.
ISBN: 978-1-449-34485-6
LSI
Table of Contents
Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
1. Introduction to AngularJS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Concepts 1
Client-Side Templates 2
Model View Controller (MVC) 3
Data Binding 3
Dependency Injection 5
Directives 5
An Example: Shopping Cart 6
Up Next 9
iii
Performance Considerations in watch() 31
Organizing Dependencies with Modules 33
How Many Modules Do I Need? 36
Formatting Data with Filters 37
Changing Views with Routes and $location 38
index.html 39
list.html 39
detail.html 40
controllers.js 40
Talking to Servers 41
Changing the DOM with Directives 43
index.html 44
controllers.js 44
Validating User Input 45
Moving On 46
3. Developing in AngularJS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Project Organization 47
Tools 50
IDEs 50
Running Your Application 51
With Yeoman 51
Without Yeoman 51
Testing with AngularJS 52
Karma 52
Unit Tests 54
End-to-End/Integration Tests 55
Compilation 57
Other Awesome Tools 59
Debugging 59
Batarang 60
Yeoman: Optimizing Your Workflow 64
Installing Yeoman 65
Starting a Fresh AngularJS project 65
Running Your Server 65
Adding New Routes, Views, and Controllers 65
The Testing Story 66
Building Your Project 66
Integrating AngularJS with RequireJS 67
iv | Table of Contents
Relationship Between Model, Controller, and Template 78
The Model 79
Controllers, Directives, and Services, Oh My! 80
Services 80
Directives 84
Controllers 85
The Templates 89
The Tests 95
Unit Tests 96
Scenario Tests 99
6. Directives. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
Directives and HTML Validation 119
API Overview 120
Naming Your Directive 121
The Directive Definition Object 122
Transclusion 126
Compile and Link Functions 126
Scopes 128
Manipulating DOM Elements 132
Controllers 133
Table of Contents | v
Moving On 136
Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
vi | Table of Contents
Preface
I can trace Angular’s beginnings to 2009, on a project called Google Feedback. We’d
gone through months of frustration with our development speed and ability to write
testable code. At around the six month mark, we had around 17,000 lines of front-end
code. At that point, one of the team members, Misko Hevery, made a bold statement
that he’d be able to rewrite the whole thing in two weeks using an open source library
that he’d created as a hobby.
I figured that a two week delay couldn’t hurt us that much and we’d at least be entertained
by Misko scrambling to build something. Misko missed his time estimate. It took three
weeks. We were all astounded, but even more astounding was that the line count for
this new app had dropped from 17,000 to a mere 1,500. It seemed that Misko was onto
something worth pursuing.
Misko and I decided we’d built a team around the concepts he started with a simple
charter: to simplify the web developer’s experience. Shyam Seshadri, this book’s co-
author, went on to lead the Google Feedback team in developing Angular’s first shipping
application.
Since then, we’ve developed Angular with guidance both from teams at Google and
from hundreds of open source contributors around the world. Thousands of developers
rely on Angular in their daily work and contribute to an amazing support network.
We’re excited to learn what you’ll teach us.
vii
Constant width
Used for program listings, as well as within paragraphs to refer to program elements
such as variable or function names, databases, data types, environment variables,
statements, and keywords.
Constant width bold
Shows commands or other text that should be typed literally by the user.
Constant width italic
Shows text that should be replaced with user-supplied values or by values deter‐
mined by context.
viii | Preface
Safari® Books Online
Safari Books Online is an on-demand digital library that delivers ex‐
pert content in both book and video form from the world’s leading
authors in technology and business.
Technology professionals, software developers, web designers, and business and crea‐
tive professionals use Safari Books Online as their primary resource for research, prob‐
lem solving, learning, and certification training.
Safari Books Online offers a range of product mixes and pricing programs for organi‐
zations, government agencies, and individuals. Subscribers have access to thousands of
books, training videos, and prepublication manuscripts in one fully searchable database
from publishers like O’Reilly Media, Prentice Hall Professional, Addison-Wesley Pro‐
fessional, Microsoft Press, Sams, Que, Peachpit Press, Focal Press, Cisco Press, John
Wiley & Sons, Syngress, Morgan Kaufmann, IBM Redbooks, Packt, Adobe Press, FT
Press, Apress, Manning, New Riders, McGraw-Hill, Jones & Bartlett, Course Technol‐
ogy, and dozens more. For more information about Safari Books Online, please visit us
online.
How to Contact Us
Please address comments and questions concerning this book to the publisher:
We have a web page for this book, where we list errata, examples, and any additional
information. You can access this page at http://oreil.ly/angularJS.
To comment or ask technical questions about this book, send email to bookques
tions@oreilly.com.
For more information about our books, courses, conferences, and news, see our website
at http://www.oreilly.com.
Find us on Facebook: http://facebook.com/oreilly
Follow us on Twitter: http://twitter.com/oreillymedia
Watch us on YouTube: http://www.youtube.com/oreillymedia
Preface | ix
Acknowledgments
We’d like to give special thanks to Misko Hevery, father of Angular, for having the
courage to think very differently about how we write web applications and to drive it
into reality; to Igor Minar for bringing stability and structure to the Angular project
and for building the roots of today’s awesome open source community; to Vojta Jina for
creating many parts of Angular, and for giving us the fastest test runner the world has
ever seen; to Naomi Black, John Lindquist, and Mathias Matias Niemelä for their expert
editing assistance. And finally, thank you to the Angular community for their contri‐
butions, and for teaching us about making Angular great through feedback from build‐
ing real applications.
x | Preface
CHAPTER 1
Introduction to AngularJS
Our ability to create amazing web-based apps is incredible, but the complexity involved
in making these apps is similarly incredible. We on the Angular team wanted to relieve
the pain involved with developing AJAX applications. At Google, we’d worked through
the hard lessons of building large web applications like Gmail, Maps, Calendar, and
several others. We thought we might be able to use these experiences to benefit everyone.
We wanted writing web apps to feel more like the first time we wrote a few lines of code
and stood back in amazement at what we’d made happen. We wanted the coding process
to feel more like creating and less like trying to satisfy the strange inner workings of
web browsers.
At the same time, we wanted an environment that helped us make the design choices
that make apps easy to create and understand from the start, but that continue to be the
right choices to make our apps easy to test, extend, and maintain as they grow large.
We’ve tried to do this in the Angular framework. We’re very excited about the results
we’ve achieved. A lot of credit goes to the open source community around Angular who
do a fantastic job supporting each other and who have taught us many things. We hope
you’ll join our community and help us learn how Angular can be even better.
Some of the larger and more involved examples and code snippets are available on a
GitHub repository for you to look at, fork, and play with at our GitHub page.
Concepts
There are a few core ideas that you’ll use throughout an Angular app. As it turns out,
we didn’t invent any of these. Instead, we’ve borrowed heavily from successful idioms
in other development environments and implemented them in a way that embraces
HTML, browsers, and many other familiar web standards.
1
Client-Side Templates
Multi-page web applications create their HTML by assembling and joining it with data
on the server, and then shipping the finished pages up to the browser. Most single-page
applications—also known as AJAX apps—do this as well, to some extent. Angular is
different in that the template and data get shipped to the browser to be assembled there.
The role of the server then becomes only to serve as static resources for the templates
and to properly serve the data required by those templates.
Let’s see an example of what assembling this data and template on the browser looks
like in Angular. We’ll take the obligatory Hello, World example, but instead of writing
“Hello, World” as a single string, let’s structure the greeting “Hello” as data that we could
change later.
For it, we’ll create our template in hello.html:
<html ng-app>
<head>
<script src="angular.js"></script>
<script src="controllers.js"></script>
</head>
<body>
<div ng-controller='HelloController'>
<p>{{greeting.text}}, World</p>
</div>
</body>
</html>
And our logic in controllers.js:
function HelloController($scope) {
$scope.greeting = { text: 'Hello' };
}
Loading hello.html into any browser will then produce what we see in Figure 1-1:
There are a few interesting things to note here in comparison with most methods in
widespread use today:
• There are no classes or IDs in the HTML to identify where to attach event listeners.
• When HelloController set the greeting.text to Hello, we didn’t have to register
any event listeners or write any callbacks.
We’ll look at more differences soon, but it should be clear already that Angular appli‐
cations are structured very differently than similar applications were in the past.
Why have we made these design choices and how does Angular work? Let’s look at some
good ideas Angular stole from elsewhere.
Data Binding
Before AJAX single-page apps were common, platforms like Rails, PHP, or JSP helped
us create the user interface (UI) by merging strings of HTML with data before sending
it to the users to display it.
Concepts | 3
Libraries like jQuery extended this model to the client and let us follow a similar style,
but with the ability to update, part of the DOM separately, rather than updating the
whole page. Here, we merge template HTML strings with data, then insert the result
where we want it in the DOM by setting innerHtml on a placeholder element.
This all works pretty well, but when you want to insert fresher data into the UI, or change
the data based on user input, you need to do quite a bit of non-trivial work to make sure
you get the data into the correct state, both in the UI and in JavaScript properties.
But what if we could have all this work done for us without writing code? What if we
could just declare which parts of the UI map to which JavaScript properties and have
them sync automatically? This style of programming is called data binding. We included
it in Angular because it works great with MVC to eliminate code when writing your
view and model. Most of the work in moving data from one to the other just happens
automatically.
To see this in action, let’s take the first example and make it dynamic. As is, the Hello
Controller sets the model greeting.text once and it never changes from then on. To
make it live, let’s change the example by adding a text input that can change the value
of greeting.text as the user types.
Here’s the new template:
<html ng-app>
<head>
<script src="angular.js"></script>
<script src="controllers.js"></script>
</head>
<body>
<div ng-controller='HelloController'>
<input ng-model='greeting.text'>
<p>{{greeting.text}}, World</p>
</div>
</body>
</html>
If we replace Hello with Hi in the input field, we’d see the screen captured in Figure 1-3.
Without ever registering a change listener on the input field, we have a UI that will
dynamically update. The same would be true for changes coming to and from the server.
In our controller, we could make a request to our server, get the response, and set
$scope.greeting.text to equal what it returns. Angular would automatically update
both the input and the text in the curly braces to that value.
Dependency Injection
We mentioned it before, but it bears repeating that there’s a lot going on with Hello
Controller that we didn’t have to write. For example, the $scope object that does our
data binding is passed to us automatically; we didn’t have to create it by calling any
function. We just asked for it by putting it in HelloController’s constructor.
As we’ll find out in later chapters, $scope isn’t the only thing we can ask for. If we want
to data bind to the location URL in the user’s browser, we can ask for an object that
manages this by putting $location in our constructor, like so:
function HelloController($scope, $location) {
$scope.greeting = { text: 'Hello' };
// use $location for something good here...
}
We get this magical effect through Angular’s dependency injection system. Dependency
injection lets us follow a development style in which, instead of creating dependencies,
our classes just ask for what they need.
This follows a design pattern called the Law of Demeter, also known as the principle of
least knowledge. Since our HelloController’s job is to set up the initial state for the
greeting model, this pattern would say that it shouldn’t worry about anything else, like
how $scope gets created, or where to find it.
This feature isn’t just for objects created by the Angular framework. You can write the
rest of this code as well.
Directives
One of the best parts of Angular is that you can write your templates as HTML. You can
do this because at the core of the framework we’ve included a powerful DOM trans‐
formation engine that lets you extend HTML’s syntax.
Concepts | 5
We’ve already seen several new attributes in our templates that aren’t part of the HTML
specification. Examples include the double-curly notation for data binding, ng-
controller for specifying which controller oversees which part of the view, and ng-
model, which binds an input to part of the model. We call these HTML extension
directives.
Angular comes with many directives that help you define the view for your app. We’ll
see more of them soon. These directives can define what we commonly view as the
template. They can declaratively set up how your application works or be used to create
reusable components.
And you’re not limited to the directives that Angular comes with. You can write your
own to extend HTML’s template abilities to do anything you can dream of.
$scope.remove = function(index) {
$scope.items.splice(index, 1);
}
}
</script>
</body>
</html>
The following is a brief tour of what’s going on here. The rest of the book is dedicated
to a more in-depth explanation.
Let’s start at the top:
<html ng-app>
The ng-app attribute tells Angular which parts of the page it should manage. Since we’ve
placed it on the <html> element, we’re telling Angular that we want it to manage the
whole page. This will often be what you want, but you might want to place it on a <div>
within the app if you’re integrating Angular with an existing app that uses other methods
to manage the page.
<body ng-controller='CartController'>
In Angular, you manage areas of the page with JavaScript classes called controllers. By
including a controller in the body tag, I’m declaring that CartController will manage
everything between <body> and </body>.
<div ng-repeat='item in items'>
The ng-repeat says to copy the DOM inside this <div> once for every element in an
array called items. On every copy of the div, it will also set a property named item to the
current element so we can use it in the template. As you can see, this results in three
<div>s each, containing the product title, quantity, unit price, total price, and a button
to remove the item entirely.
<span>{{item.title}}</span>
As we showed in the “Hello, World” example, data binding via {{ }} lets us insert the
value of a variable into part of the page and keep it in sync. The full expression
{{item.title}} retrieves the current item in the iteration and then inserts the contents
of that item’s title property into the DOM.
<input ng-model='item.quantity'>
The ng-model definition creates data binding between the input field and the value of
item.quantity.
This CartController manages the logic of the shopping cart. We’ll tell Angular that
the controller needs something called $scope by putting it here. The $scope is what lets
us bind data to elements in the UI.
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
By defining $scope.items, I’ve created a dummy data hash to represent the collection
of items in the user’s shopping cart. We want to make them available to data bind with
the UI, so we’ll add them to $scope.
Of course, a real version of this can’t just work in memory, and will need to talk to a
server to properly persist the data. We’ll get to that in later chapters.
$scope.remove = function(index) {
$scope.items.splice(index, 1);
}
We want the remove() function available to bind in the UI, so we’ve added this to $scope
as well. For the in-memory version of the shopping cart, the remove() function can just
delete items from the array. Because the list of <div>s created by ng-repeat is data
Up Next
We’ve looked at just the most basic idioms in Angular and some very simple examples.
The rest of the book is dedicated to showing off what the framework has to offer.
Up Next | 9
CHAPTER 2
Anatomy of an AngularJS Application
Unlike typical libraries where you pick and choose functions as you like, everything in
Angular is designed to be used as a collaborative suite. In this chapter we’ll cover all of
the basic building blocks in Angular so you can understand how they fit together. Many
of these blocks will be covered in more detail in later chapters.
Invoking Angular
Any application must do two things to start Angular:
11
Declaring Angular’s Boundaries with ng-app
The ng-app directive lets you tell Angular which part of your page it should expect to
manage. If you’re building an all-Angular application, you should include ng-app as
part of the <html> tag, like so:
<html ng-app>
…
</html>
This tells Angular to manage all DOM elements in the page.
If you’ve got an existing app where some other technology expects to manage the DOM,
such as Java or Rails, you can tell Angular to manage only a part of the page by placing
it on some element like a <div> within the page.
<html>
…
<div ng-app>
…
</div>
…
</html>
• A model containing data that represents the current state of your application.
• Views that display this data.
• Controllers that manage the relationship between your model and your views.
You’ll create your model using object attributes, or even just primitive types containing
your data. There’s nothing special about model variables. If you want to display some
text to the user, you could have a string, like so:
var someText = 'You have started your journey.';
You create your views by writing a template as an HTML page and merging it with data
from your model. As we’ve seen, you can insert a placeholder in the DOM and set its
text like this:
<p>{{someText}}</p>
We call this double-curly syntax interpolation, as it inserts new content into an existing
template.
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
function TextController($scope) {
$scope.someText = 'You have started your journey.';
}
</script>
</body>
</html>
Loading this in a browser, you would see:
You have started your journey.
Though this primitive-style model works in simple cases, for most applications you’ll
want to create a model object to contain your data. We’ll create a messages model object
and use it to store our someText. So instead of:
var someText = 'You have started your journey.';
you would write:
var messages = {};
messages.someText = 'You have started your journey.';
function TextController($scope) {
$scope.messages = messages;
}
and use it in your template as:
<p>{{messages.someText}}</p>
As we’ll see later when we discuss the $scope object, creating a model object like this
will prevent unexpected behavior that could be caused by the prototypal inheritance in
$scope objects.
While we’re discussing practices that will save you in the long run, in the previous
example, we’ve created TextController in the global scope. While this is fine for ex‐
amples, the right way to define a controller is as part of something called a module,
<script
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js">
</script>
<script>
var myAppModule = angular.module('myApp', []);
myAppModule.controller('TextController',
function($scope) {
var someText = {};
someText.message = 'You have started your journey.';
$scope.someText = someText;
});
</script>
</body>
</html>
In this version, we told our ng-app element about the name of our module, myApp. We
then called the Angular object to create a module named myApp and pass our controller’s
function to a call to that module’s controller function.
We’ll get to all the whys and hows of modules in a bit. For now, just remember that
keeping things out of the global namespace is a good thing and that modules are the
mechanism we use to do so.
Here, it stamps out a copy of the outer <div>, and everything inside it, once for every
element in the items array.
Steps 1 through 3 are standard for every Angular app. It’s in steps 4 and 5 that you have
choices. These steps can happen synchronously or asynchronously. For performance,
the data your app needs to display to the user on the first view can come down with the
HTML template to avoid multiple requests.
By structuring your application with Angular, your application’s templates are kept sep‐
arate from the data that populates them. The result of this is that these templates are
now cacheable. Only new data need come down to the browser after the first load. Just
as with JavaScript, images, CSS, and other resources, caching these templates can give
your application even better performance.
Displaying Text
You can display and update text anywhere in your UI using the ng-bind directive. It has
two equivalent forms. One we’ve seen with double-curly braces:
<p>{{greeting}}</p>
Form Inputs
Working with form elements in Angular is simple. As we’ve seen in several examples,
you can use the ng-model attribute to bind elements to your model properties. This
works with all the standard form elements like text inputs, radio buttons, checkboxes,
and so on. We can bind a checkbox to a property like so:
<form ng-controller="SomeController">
<input type="checkbox" ng-model="youCheckedIt">
</form>
This means that:
1. When the user checks the box, a property called youCheckedIt on the SomeCon
troller’s $scope will become true. Unchecking the box makes youCheckedIt false.
2. If you set $scope.youCheckedIt to true in SomeController, the box becomes
checked in the UI. Setting it to false unchecks the box.
Now let’s say we actually want to take action when the user does something. For input
elements, you use the ng-change attribute to specify a controller method that should be
called whenever the user changes the input’s value. Let’s do a simple calculator to help
startup owners understand how much money they need to get going:
<form ng-controller="StartUpController">
Starting: <input ng-change="computeNeeded()"
ng-model="funding.startingEstimate">
Recommendation: {{funding.needed}}
</form>
For our simplistic example, let’s just set the output to be ten times the user’s estimate.
We’ll also set a default value of zero to start:
$scope.computeNeeded = function() {
$scope.needed = $scope.startingEstimate * 10;
};
}
There is, however, a potential problem with the strategy in the preceding code. The issue
is that we’re only recomputing the needed amount when users type in the input field.
This works fine if this input field is only ever updated when users type in this particular
input. But what if other inputs bind to this property in the model? What if it gets updated
when data comes in from the server?
To update the field no matter how it gets updated, we want to use a $scope function
called $watch(). We’ll talk about watch in detail later in this chapter. The basics are that
you can call $watch() with an expression to observe and a callback that gets invoked
whenever that expression changes.
In this case, we want to watch funding.startingEstimate and call computeNeeded()
whenever it changes. We could then rewrite the StartUpController to use this tech‐
nique:
function StartUpController($scope) {
$scope.funding = { startingEstimate: 0 };
computeNeeded = function() {
$scope.funding.needed = $scope.funding.startingEstimate * 10;
};
$scope.$watch('funding.startingEstimate', computeNeeded);
}
Note that the expression to watch is in quotes. Yes, it is a string. This string is evaluated
as something called an Angular expression. Expressions can do simple operations and
have access to the properties in the $scope object. We’ll cover expressions more later in
this chapter.
You could also watch the return value of a function, but it won’t work to watch the
property funding.startingEstimate as this evaluates to zero, its initial value, and that
zero never changes.
Then, because our funding.needed will automatically update whenever funding.star
tingEstimate changes, we can write a simpler template, like so:
<form ng-controller="StartUpController">
Starting: <input ng-model="funding.startingEstimate">
Recommendation: {{funding.needed}}
</form>
$scope.requestFunding = function() {
window.alert("Sorry, please get more customers first.");
};
}
The ng-submit directive also automatically prevents the browser from doing its default
POST action when it tries to submit the form.
To handle other event cases, like when you want to provide interactions that don’t submit
a form, Angular provides event-handling directives that resemble the browser’s native
event attributes. For onclick, you’d use ng-click. For ondblclick, use ng-dblclick,
and so on.
We can try this out by extending our startup calculator one last time with a reset button
that will reset the input value to zero:
<form ng-submit="requestFunding()" ng-controller="StartUpController">
Starting: <input ng-change="computeNeeded()" ng-model="startingEstimate">
Recommendation: {{needed}}
<button>Fund my startup!</button>
<button ng-click="reset()">Reset</button>
</form>
function StartUpController($scope) {
$scope.computeNeeded = function() {
$scope.needed = $scope.startingEstimate * 10;
};
$scope.requestFunding = function() {
window.alert("Sorry, please get more customers first.");
};
$scope.reset = function() {
1. Not everyone’s browser supports JavaScript. Let everyone see all of your content
and use your app without needing to execute code in the browser.
2. Some folks use browsers that work differently. Visually impaired folks who use
screen-readers and some mobile phone users can’t use sites with JavaScript.
3. Javascript works differently across different platforms. IE is usually the culprit here.
You need to put in different event-handling code depending on the browser.
4. These event handlers reference functions in the global namespace. It will cause you
headaches when you try to integrate other libraries with functions of the same
names.
5. These event handlers combine structure and behavior. This makes your code more
difficult to maintain, extend, and understand.
In most ways, life was better when you wrote JavaScript in this style. One thing that was
not better, however, was code complexity and readability. Instead of declaring your event
handler actions with the element they act on, you usually had to assign IDs to these
elements, get a reference to the element, and set up event handlers with callbacks. You
could invent a structure to only create these associations in well-known locations, but
most apps ended up with these handler setups littered all over.
In Angular, we decided to reexamine the problem.
The world has changed since these concepts were born. Point #1 is no longer true for
any interesting population. If you’re running a browser without JavaScript, you’re rele‐
gated to sites created in the 1990s. As for point #2, modern screen-readers have caught
up. With proper use of ARIA semantic tags, you can make very rich UIs easily accessible.
Mobile phones now run JavaScript on par with desktop machines.
So now the question is: could we solve #3 and #4 while regaining the readability and
simplicity of the inline technique?
As previously mentioned, for most inline event handlers, Angular has an equivalent in
the form of ng-eventhandler="expression" where eventhandler would be replaced
• Behave the same in every browser. Angular takes care of the differences for you.
• Do not operate on the global namespace. The expressions you specify can only
access functions and data that is in the scope of the element’s controller.
This last point may sound a little cryptic, so let’s look at an example. In a typical app,
you would create a nav bar and a content area that changes as you select different menu
options from the nav. We could write the skeleton for it, like so:
<div class="navbar" ng-controller="NavController">
…
<li class="menu-item" ng-click="doSomething()">Something</li>
…
</div>
Here both the <li> in the navbar and the <div> in the content area call a function called
doSomething() when a user clicks on them. As the developer, you set up the function
that these calls refer to in your controller code. They could be the same function or
different ones:
function NavController($scope) {
$scope.doSomething = doA;
}
function ContentAreaController($scope) {
$scope.doSomething = doB;
}
Here, doA() and doB() functions can be the same or different, as you define them.
We’re now left with point #5, combining structure and behavior. This is a hand-wavy
argument, as you can’t point to any concrete negative outcomes, but it’s very similar to
a real problem we had in mind, combining responsibilities of the presentation and your
application logic. This certainly does have the negative side effects that folks talk about
when describing the issue labeled as structure/behavior.
function StudentListController($scope) {
$scope.students = students;
}
To display this list of students, we can do something like the following:
<ul ng-controller=''>
<li ng-repeat='student in students'>
<a href='/student/view/{{student.id}}'>{{student.name}}</a>
</li>
</ul>
The ng-repeat will make a copy of all of the HTML inside the tag, including the tag it’s
placed on. With this, we would see:
• Mary Contrary
• Jack Sprat
function StudentListController($scope) {
$scope.students = students;
$scope.insertTom = function () {
$scope.students.splice(1, 0, {name:'Tom Thumb', id:'4'});
};
}
and adding a button to invoke it in the template:
<ul ng-controller=''>
<li ng-repeat='student in students'>
<a href='/student/view/{{student.id}}'>{{student.name}}</a>
</li>
</ul>
<button ng-click="insertTom()">Insert</button>
we now see:
• Mary Contrary
• Tom Thumb
• Jack Sprat
• Jill Hill
The ng-repeat directive also gives you references to the index of the current element
via $index, and booleans that tell you if you’re on the first element, somewhere in the
middle, or the last element of the collection with $first, $middle, and $last.
You might imagine using the $index to label rows in a table. Given a template like this:
<table ng-controller='AlbumController'>
<tr ng-repeat='track in album'>
<td>{{$index + 1}}</td>
<td>{{track.name}}</td>
<td>{{track.duration}}</td>
</tr>
</table>
and this controller:
function AlbumController($scope) {
$scope.album = album;
}
We get the following:
1 Southwest Serenade 2:34
2 Northern Light Waltz 3:21
3 Eastern Tango 17:45
$scope.toggleMenu = function() {
$scope.menuState.show = !$scope.menuState.show;
};
you could show the stun function of your DeathRay as disabled with this template:
<div ng-controller='DeathrayMenuController'>
<ul>
<li class='menu-disabled-{{isDisabled}}' ng-click='stun()'>Stun</li>
...
</ul>
<div/>
where you’d set the isDisabled property via your controller as appropriate:
function DeathrayMenuController($scope) {
$scope.isDisabled = false;
$scope.stun = function() {
// stun the target, then disable menu to allow regeneration
$scope.isDisabled = 'true';
};
}
The class on the stun menu item will be set to menu-disabled- plus the value of
$scope.isDisabled. As this is initially false, the result will be menu-disabled-false.
As there’s no CSS rule that matches, there will be no effect. When $scope.isDisa
bled is set to true, the CSS rule becomes menu-disabled-true, which invokes the rule
to make the text gray.
This technique works equally well when combining inline styles with interpolation, such
as with style="{{some expression}}".
While kind of clever, this technique has the drawback of using a level of indirection in
composing your class names. While you can easily understand it in this small example,
it can quickly become unmanageable having to read both your template and JavaScript
to correctly create your CSS.
Because of this, Angular provides the ng-class and ng-style directives. Each of them
takes an expression. The result of evaluating this expression can be one of the following:
Let’s imagine that you want to display errors and warnings to your users in a standard
location in the application’s header. Using the ng-class directive, you could do some‐
thing like this:
.error {
background-color: red;
}
.warning {
background-color: yellow;
}
<div ng-controller='HeaderController'>
...
<div ng-class='{error: isError, warning: isWarning}'>{{messageText}}</div>
…
<button ng-click='showError()'>Simulate Error</button>
<button ng-click='showWarning()'>Simulate Warning</button>
</div>
function HeaderController($scope) {
$scope.isError = false;
$scope.isWarning = false;
$scope.showError = function() {
$scope.messageText = 'This is an error!';
$scope.isError = true;
$scope.isWarning = false;
};
$scope.showWarning = function() {
$scope.messageText = 'Just a warning. Please carry on.';
$scope.isWarning = true;
$scope.isError = false;
};
}
You can even do nifty things like highlighting a selected row in a table. Let’s say we’re
building a restaurant directory and we want to highlight a row that the user clicks on.
In our CSS, we set up the style for a highlighted row:
.selected {
background-color: lightgreen;
}
In our JavaScript, we just set up some dummy restaurants and create the selectRow
function:
function RestaurantTableController($scope) {
$scope.directory = [{name:'The Handsome Heifer', cuisine:'BBQ'},
{name:'Green's Green Greens', cuisine:'Salads'},
{name:'House of Fine Fish', cuisine:'Seafood'}];
$scope.selectRestaurant = function(row) {
$scope.selectedRow = row;
};
}
Instead, you should use the ng-src attribute and write your template as:
<img ng-src="/images/cats/{{favoriteCat}}">
Expressions
The goal behind the expressions that you use in templates is to let you be as clever as
you need to be to create hooks between your template, your application logic, and your
data, but at the same time prevent application logic from sneaking into the template.
Until this point, we’ve been mostly using references to data primitives as the expressions
passed to Angular directives. But these expressions can do much more. You can do
The first expression here, recompute() / 10, while valid, is a good example of putting
logic in the template, and should be avoided. Keeping a separation of responsibilities
between your view and controllers ensures that they’re easy to reason and easy to test.
While you can do quite a lot with expressions, they are computed with a custom parser
that’s part of Angular. They are not evaluated using JavaScript’s eval(), and are con‐
siderably more restrictive.
Instead, they are evaluated using a custom parser that comes with Angular. In it, you
won’t find looping constructs (for, while, and so on), flow-of-control operators (if-else,
throw) or operators that modify data (++, --). When you need these types of operations,
do them in your controller or via a directive.
Though expressions are more restrictive than JavaScript in many ways, they are more
forgiving to undefined and null. Instead of throwing a NullPointerException error,
templates will simply render nothing. This allows you to safely use model values that
haven’t been set yet, and have them appear in the UI as soon as they get populated.
We’ve seen many examples of the first two in this chapter already. We’ll get to that last
one in a bit. The conceptual purpose of controllers, however, is to provide the code or
logic to execute the user’s wishes as they interact with the view.
To keep your controllers small and manageable, our recommendation is that you create
one controller per functional area in your view. That is, if you have a menu, create a
2. Using ng-model on a form input. As with expressions, the model specified as the
argument for ng-model also works within the scope of the enclosing controller. The
one addition is that this creates a bi-directional data binding between the form field
state and your specified model.
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
$scope.totalCart = function() {
var total = 0;
for (var i = 0, len = $scope.items.length; i < len; i++) {
total = total + $scope.items[i].price * $scope.items[i].quantity;
}
return total;
}
$scope.subtotal = function() {
return $scope.totalCart() - $scope.discount;
};
$scope.$watch($scope.totalCart, calculateDiscount);
}
Then Angular runs all of these again, bringing us to six. Angular does this to verify that
transitive changes in your model have fully propagated and your model has settled.
Angular does this checking by making a copy of all watched properties and comparing
them to the current value to see if they’ve changed. In fact, Angular may run this up to
ten times to ensure full propagation. If changes are still occurring after ten iterations,
Angular exits with an error. If that occurs, you probably have a dependency loop that
you’ll need to fix.
Though you currently need to worry about this, by the time you’ve finished this book
it may be a non-issue. While Angular has had to implement data binding in JavaScript,
we’ve been working with the TC39 folks on a low-level native implementation called
Object.observe(). With this in place, Angular will automatically use Object.ob
serve() wherever present to give you native-speed data binding.
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
Notice here that the $watch specified items as a string. This is possible because the
$watch function can take either a function (as we did previously) or a string. If a string
is passed to the $watch function, then it will be evaluated as an expression in the scope
of the $scope it’s called on.
This strategy might work well for your app. However, since we’re watching the items
array, Angular will have to make a copy of it to compare it for us. For a large list of items,
it may perform better if we just recalculate the bill properties every time Angular eval‐
uates the page. We can do this by creating a $watch with only a watchFn that will recal‐
culate our properties like this:
$scope.$watch(function() {
var total = 0;
In the first case, if you’ve got an object with two properties a and b in your scope, and
want to execute the callMe() function on change, you could watch both of them, like
so:
$scope.$watch('things.a + things.b', callMe(...));
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.
• 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();
}
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', []);
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) {
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>
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', []);
// 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
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>
appModule.directive('ngbkFocus', function() {
return {
link: function(scope, element, attrs, controller) {
element[0].focus();
}
};
});
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';
}
}
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.
$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.
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:
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
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.
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.
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
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.
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 = {
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
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.
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
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.
• 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!
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.
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
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.
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
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
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.
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.
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?
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!
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).
• 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:
All of this can be accomplished in a single step in Yeoman with the following command:
yeoman init angular:route routeName
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
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.
</head>
<body class="home-page" ng-controller="RootController">
<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
// 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},
// level of logging
logLevel = LOG_INFO;
// enable/disable watching file and executing tests whenever any file changes
autoWatch = true;
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'
// 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);
}));
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 = '../';
// 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/'
};
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:
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
Figure 4-1. GutHub: A simple recipe management application
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:
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
{
"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.
Services
// This file is app/scripts/services/services.js
services.factory('Recipe', ['$resource',
function($resource) {
return $resource('/recipes/:id', {id: '@id'});
}]);
• Recipe.get()
• Recipe.save()
• Recipe.query()
• Recipe.remove()
• Recipe.delete()
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.
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
behave very similarly. The flow of both these services is as follows:
1. Create a $q deferred object (these are AngularJS promises, used for chaining asyn‐
chronous functions).
2. Make a call to the server.
3. Resolve the deferred object when the server returns the value.
4. Return the promise that will be used by the routing mechanism of AngularJS.
fetchServerConfig(function(serverConfig) {
fetchUserProfiles(serverConfig.USER_PROFILES, username,
function(profiles) {
currentProfile = profiles.currentProfile;
Promises solve these issues. Before we go into the how, let’s take a look at the same
problem implemented with promises:
var currentProfile =
fetchServerConfig().then(function(serverConfig) {
return fetchUserProfiles(serverConfig.USER_PROFILES, username);
}).then(function(profiles) {
return profiles.currentProfile;
}, function(error) {
// Handle errors in either fetchServerConfig or
// fetchUserProfiles here
});
Notice the advantages:
1. You can chain function calls, so you don’t get into an indentation nightmare.
2. You are assured that the previous function call is finished before the next function
in the chain is called.
3. Each then() call takes two arguments (both functions). The first one is the success
callback and the second one is the error handler.
4. In case of errors in the chain, the error will get propagated through to the rest of
the error handlers. So any error in any of the callbacks can be handled in the end.
What about resolve and reject, you ask? Well, deferred in AngularJS is a way of
creating promises. Calling resolve on it fulfills the promise (calls the success handler),
while calling reject on it calls the error handler of the promise.
directives.directive('butterbar', ['$rootScope',
function($rootScope) {
return {
link: function(scope, element, attrs) {
element.addClass('hide');
$rootScope.$on('$routeChangeStart', function() {
element.removeClass('hide');
});
$rootScope.$on('$routeChangeSuccess', function() {
element.addClass('hide');
});
}
};
}]);
directives.directive('focus',
function() {
return {
link: function(scope, element, attrs) {
element[0].focus();
}
};
});
The preceding directive returns an object with a single property, link. We will dive
deeper into how you can create your own directives in Chapter 6, but for now, all you
need to know is the following:
It basically hides the element right up front, then adds two watches on the root scope.
Every time a route change begins, it shows the element (by changing its class), and every
time the route has successfully finished changing, it hides the butterbar again.
Another interesting thing to note is how we inject the $rootScope into the directive.
All directives directly hook into the AngularJS dependency injection system, so you can
inject your services and whatever else you need into them.
The final thing of note is the API for working with the element. jQuery veterans will be
glad to know that it follows a jQuery-like syntax (addClass, removeClass). AngularJS
implements a subset of the calls of jQuery so that jQuery is an optional dependency for
any AngularJS project. In case you do end up using the full jQuery library in your project,
you should know that AngularJS uses that instead of the jQlite implementation it has
built-in.
The second directive (focus) is much simpler. It just calls the focus() method on the
current element. You can call it by adding the focus attribute on any input element, like
so:
<input type="text" focus></input>
When the page loads, that element immediately gets the focus.
Controllers
With directives and services covered, we can finally get into the controllers, of which
we have five. All these controllers are located in a single file (app/scripts/controllers/
controllers.js), but we’ll go over them one at a time. Let’s go over the first controller,
which is the List Controller, responsible for displaying the list of all recipes in the system.
app.controller('ListCtrl', ['$scope', 'recipes',
function($scope, recipes) {
$scope.edit = function() {
$location.path('/edit/' + recipe.id);
};
}]);
The interesting aspect about the View Controller is the edit function it exposes on the
scope. Instead of showing and hiding fields or something similar, this controller relies
on AngularJS to do the heavy lifting (as should you!). The edit function simply changes
the URL to the edit equivalent for the recipe, and lo and behold, AngularJS does the
rest. AngularJS recognizes that the URL has changed and loads the corresponding view
(which is the same recipe in edit mode). Voila!
Next, let’s take a look at the Edit Controller:
app.controller('EditCtrl', ['$scope', '$location', 'recipe',
function($scope, $location, recipe) {
$scope.recipe = recipe;
$scope.save = function() {
$scope.recipe.$save(function(recipe) {
$location.path('/view/' + recipe.id);
});
};
$scope.remove = function() {
delete $scope.recipe;
$location.path('/');
};
}]);
What’s new here are the save and remove methods that the Edit Controller exposes on
the scope.
The save function on the scope does what you would expect it to. It saves the current
recipe, and once it is done saving, redirects the user to the view screen with the same
The remove function is also straightforward, in that it removes the recipe from the
scope, and redirects users to the main landing page. Note that it doesn’t actually remove
it from our server, though it shouldn’t be very hard to make that additional call.
Next, we have the New Controller:
app.controller('NewCtrl', ['$scope', '$location', 'Recipe',
function($scope, $location, Recipe) {
$scope.recipe = new Recipe({
ingredients: [ {} ]
});
$scope.save = function() {
$scope.recipe.$save(function(recipe) {
$location.path('/view/' + recipe.id);
});
};
}]);
The New Controller is almost exactly the same as the Edit Controller. In fact, you could
look at combining the two into a single controller as an exercise. The only major dif‐
ference is that the New Controller creates a new recipe (which is a resource, so that it
has the save function) as the first step. Everything else remains unchanged.
Finally, we have the Ingredients Controller. This is a special controller, but before we
get into why or how, let’s take a look:
app.controller('IngredientsCtrl', ['$scope', function($scope) {
$scope.addIngredient = function() {
var ingredients = $scope.recipe.ingredients;
ingredients[ingredients.length] = {};
};
$scope.removeIngredient = function(index) {
$scope.recipe.ingredients.splice(index, 1);
};
}]);
All the other controllers that we saw so far are linked to particular views on the UI. But
the Ingredients Controller is special. It’s a child controller that is used on the edit pages
to encapsulate certain functionality that is not needed at the higher level. The interesting
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/', {
controller: 'ListCtrl',
resolve: {
recipes: function(MultiRecipeLoader) {
return MultiRecipeLoader();
}
},
templateUrl:'/views/list.html'
}).when('/edit/:recipeId', {
controller: 'EditCtrl',
resolve: {
recipe: function(RecipeLoader) {
return RecipeLoader();
}
},
templateUrl:'/views/recipeForm.html'
}).when('/view/:recipeId', {
controller: 'ViewCtrl',
resolve: {
recipe: function(RecipeLoader) {
return RecipeLoader();
}
},
templateUrl:'/views/viewRecipe.html'
}).when('/new', {
controller: 'NewCtrl',
templateUrl:'/views/recipeForm.html'
}).otherwise({redirectTo:'/'});
}]);
As promised, we finally reached the point where the resolve functions are used. The
previous piece of code sets up the Guthub AngularJS module, as well as the routes and
templates involved in the application.
You might notice that both the Edit and the New controller routes lead
to the same template URL, views/recipeForm.html. What’s happening
here? We reused the edit template. Depending on which controller is
associated, different elements are shown in the edit recipe template.
With this done, we can now move on to the templates, how these controllers hook up
to them, and manage what is shown to the end user.
The Templates
Let us start by taking a look at the outermost, main template, which is the index.html.
This is the base of our single-page application, and all the other views are loaded within
the context of this template:
<!DOCTYPE html>
<html lang="en" ng-app="guthub">
<head>
<title>GutHub - Create and Share</title>
<script src="scripts/vendor/angular.min.js"></script>
<script src="scripts/vendor/angular-resource.min.js"></script>
<script src="scripts/directives/directives.js"></script>
<script src="scripts/services/services.js"></script>
<script src="scripts/controllers/controllers.js"></script>
The Templates | 89
<link href="styles/bootstrap.css" rel="stylesheet">
<link href="styles/guthub.css" rel="stylesheet">
</head>
<body>
<header>
<h1>GutHub</h1>
</header>
<div butterbar>Loading...</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span2">
<!--Sidebar-->
<div id="focus"><a href="/#/new">New Recipe</a></div>
<div><a href="/#/">Recipe List</a></div>
</div>
<div class="span10">
<div ng-view></div>
</div>
</div>
</div>
</body>
</html>
There are five interesting elements to note in the preceding template, most of which
you already encountered in Chapter 2. Let’s go over them one by one:
ng-app
We set the ng-app module to be GutHub. This is the same module name we gave
in our angular.module function. This is how AngularJS knows to hook the two
together.
script tag
This is where AngularJS is loaded for the application. It has to be done before all
your JS files that use AngularJS are loaded. Ideally, this should be done at the bottom
of the body.
Butterbar
Aha! Our first usage of a custom directive. When we defined our butterbar di‐
rective before, we wanted to use it on an element so that it would be shown when
the routes were changing, and hidden on success. The highlighted element’s text is
shown (a very boring “Loading…” in this case) as needed.
Link href Values
The hrefs link to the various pages of our single-page application. Notice how they
use the # character to ensure that the page doesn’t reload, and are relative to the
current page. AngularJS watches the URL (as long as the page isn’t reloaded), and
<div>{{recipe.description}}</div>
<h3>Ingredients</h3>
The Templates | 91
<ul class="unstyled">
<li ng-repeat="ingredient in recipe.ingredients">
<span>{{ingredient.amount}}</span>
<span>{{ingredient.amountUnits}}</span>
<span>{{ingredient.ingredientName}}</span>
</li>
</ul>
<h3>Instructions</h3>
<div>{{recipe.instructions}}</div>
<div class="control-group">
<label class="control-label" for="description">Description:</label>
<div class="controls">
<textarea ng-model="recipe.description"
class="input-xlarge"
id="description"></textarea>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ingredients">Ingredients:</label>
<div class="controls">
<ul id="ingredients" class="unstyled" ng-controller="IngredientsCtrl">
<li ng-repeat="ingredient in recipe.ingredients">
<input ng-model="ingredient.amount" class="input-mini">
<input ng-model="ingredient.amountUnits" class="input-small">
<input ng-model="ingredient.ingredientName">
<button type="button"
class="btn btn-mini"
ng-click="removeIngredient($index)">
<i class="icon-minus-sign"></i> Delete </button>
</li>
<button type="button"
class="btn btn-mini"
ng-click="addIngredient()">
<i class="icon-plus-sign"></i> Add </button>
</ul>
</div>
</div>
<div class="control-group">
<label class="control-label" for="instructions">Instructions:</label>
<div class="controls">
<textarea ng-model="recipe.instructions"
class="input-xxlarge"
id="instructions"></textarea>
</div>
</div>
<div class="form-actions">
<button class="btn btn-primary">Save</button>
<button type="button"
ng-click="remove()"
ng-show="!recipe.id"
class="btn">Delete</button>
The Templates | 93
</div>
</form>
Don’t panic. It looks like a lot of code, and it is a lot of code, but if you actually dig into
it, it’s not very complicated. In fact, a lot of it is simple, repetitive boilerplate to show
editable input fields for editing recipes:
• The focus directive is added on the very first input field (the title input field).
This ensures that when the user navigates to this page, the title field has focus so
the user can immediately start typing in the title.
• The ng-submit directive is used very similarly to the previous example, so we won’t
dive into it much, other than to say that it saves the state of the recipe and signals
the end of the editing process. It hooks up to the save() function in the Edit Con‐
troller.
• The ng-model directive is used to bind the various input boxes and text areas on
the field to the model.
• One of the more interesting aspects on this page, and one we recommend you spend
some time trying to understand, is the ng-controller tag on the ingredients list
portion. Let’s take a minute to understand what is happening here.
We see a list of ingredients being displayed, and the container tag is associated with
an ng-controller. That means that the whole <ul> tag is scoped to the Ingredients
Controller. But what about the actual controller of this template, the Edit Control‐
ler? As it turns out, the Ingredients Controller is created as a child controller of the
Edit Controller, thereby inheriting the scope of Edit Controller. That is why it has
access to the recipe object from the Edit Controller.
In addition, it adds the addIngredient() method, which is used by the highlighted
ng-click, which is accessible only within the scope of the <ul> tag. Why would
you want to do this? This is the best way to separate your concerns. Why should
the Edit Controller have an addIngredients() method, when 99% of the template
doesn’t care about it? Child and nested controllers are awesome for such precise,
contained tasks, and allow you to separate your business logic into more manage‐
able chunks.
• The other directive that we want to cover in some depth here is the form validation
controls. It is easy enough in the AngularJS world to set a particular form field “as
required.” Simply add the required tag to the input (as is the case in the preceding
code). But now what do you do with it?
For that, we jump down to the Save button. Notice the ng-disabled directive on
it, which says recipeForm.$invalid. The recipeForm is the name of the form
which we have declared. AngularJS adds some special variables to it ($valid and
$invalid being just two) that allow you to control the form elements. AngularJS
We can also set the max and min length of an input, as well as a Regex pattern against
which an input field will be validated. Furthermore, there are advanced usages that can
be applied to show certain error messages only when specific conditions are met. Let
us diverge for a bit with a small example:
<form name="myForm">
User name: <input type="text"
name="userName"
ng-model="user.name"
ng-minlength="3">
<span class="error"
ng-show="myForm.userName.$error.minlength">Too Short!</span>
</form>
In the preceding example, we add a requirement that the username be at least three
characters (through the use of the ng-minlength directive). Now, the form gets popu‐
lated with each named input in its scope—we have only userName in this example—
each of which will have an $error object (which will further include what kind of error
it has or doesn’t have: required, minlength, maxlength, or pattern) and a $valid tag
to signal whether the input itself is valid or not.
We can use this to selectively show error messages to the user, depending on the type
of input error he is making, as we do in the previous example.
Jumping back to our original template—Recipe form template—there is another nice
usage of the ng-show highlighted within the ingredients repeater scope. The Add In‐
gredient button is shown only beside the last ingredient. This is accomplished by calling
an ng-show and using the special $last variable that is accessible inside a repeater
element scope.
Finally, we have the last ng-click, which is attached to the second button, used for
deleting the recipe. Notice how the button only shows if the recipe is not saved yet.
While usually it would make more sense to write ng-hide="recipe.id", sometimes it
makes more semantic sense to say ng-show="!recipe.id". That is, show if the recipe
doesn’t have an id, rather than hide if the recipe has an id.
The Tests
We have been holding off on showing you the tests that go along with the controller,
but you knew they were coming, didn’t you? In this section, we’ll go over what kinds of
tests you would write for which parts of the code, and how you would actually write
them.
The Tests | 95
Unit Tests
The first and most important kind of test is the unit test. This tests that the controllers
(and directives, and services) that you have developed are correctly structured and
written, and that they do what you would expect them to.
Before we dive into the individual unit tests, let us take a look at the test harness that
surrounds all of our controller unit tests:
describe('Controllers', function() {
var $scope, ctrl;
//you need to indicate your module in a test
beforeEach(module('guthub'));
beforeEach(function() {
this.addMatchers({
toEqualData: function(expected) {
return angular.equals(this.actual, expected);
}
});
});
});
The harness (we are still using Jasmine to write these tests in a behavioral manner) does
a few things:
1. Creates a globally (at least for the purpose of this test spec) accessible scope and
controller, so we don’t worry about creating a new variable for each controller.
2. Initializes the module that our app uses (GutHub in this case).
3. Adds a special matcher that we call equalData. This basically allows us to perform
assertions on resource objects (like recipes) that are returned through the $re
source service or RESTful calls.
With that harness in place, let’s take a look at the unit tests for the List Controller:
describe('ListCtrl', function() {
var mockBackend, recipe;
// _$httpBackend_ is the same as $httpBackend. Only written this way to
var recipes;
expect(recipes).toBeUndefined();
mockBackend.flush();
The Tests | 97
We test the MultiRecipeLoader by hooking up a mock HttpBackend in our test. This
comes from the angular-mocks.js file that is included when these tests are run. Just
injecting it into your beforeEach method is enough for you to start setting expectations
on it. In our second, more meaningful test, we set an expectation for a server GET call
to recipes, which will return a simple array of objects. We then use our new custom
matcher to ensure that this is exactly what was returned. Note the call to flush() on
the mock backend, which tells the mock backend to now return response from the
server. You can use this mechanism to test control flow and see how your application
handles before and after the server returns a response.
We will skip View Controller, as it is almost exactly like the List Controller except for
the addition of an edit() method on the scope. This is pretty simple to test, as you can
inject the $location into your test and check its value.
Let us now jump to the Edit Controller, which has two points of interest that we should
be unit testing. The resolve function is similar to the one we saw before, and can be
tested the same way. Instead, we now want to see how we can test the save() and the
remove() methods. Let’s take a look at the tests for those (assuming our harnesses from
the previous example):
describe('EditController', function() {
var mockBackend, location;
beforeEach(inject(function($rootScope,
$controller,
_$httpBackend_,
$location,
Recipe) {
mockBackend = _$httpBackend_;
location = $location;
$scope = $rootScope.$new();
ctrl = $controller('EditCtrl', {
$scope: $scope,
$location: $location,
recipe: new Recipe({id: 1, title: 'Recipe'})
});
}));
$scope.save();
expect(location.path()).toEqual('/test');
mockBackend.flush();
$scope.remove();
expect($scope.recipe).toBeUndefined();
expect(location.path()).toEqual('/');
});
});
In the first test, we test the save() function. In particular, we ensure that saving first
makes a POST request to the server with our object, and then, once the server responds,
the location is changed to the newly persisted object’s view recipe page.
The second test is even simpler. We simply check to ensure that calling remove() on the
scope removes the current recipe, then redirects the user to the main landing page. This
can be easily done by injecting the $location service into our test, and working with
it.
The rest of the unit tests for the controllers follow very similar patterns, so we can skip
over them. At their base, such unit tests rely on a few things:
• Ensuring that the controller (or more likely, the scope) reaches the correct state at
the end of the initialization
• Confirming that the correct server calls are made, and that the right state is achieved
by the scope during the server call and after it is completed (by using our mocked
out backend in the unit tests)
• Leveraging the AngularJS dependency injection framework to get a handle on the
elements and objects that the controller works with to ensure that the controller is
getting set to the correct state
Scenario Tests
Once we are happy with our unit tests, we might be tempted to just lean back, smoke a
cigar, and call it a day. But the work of an AngularJS developer isn’t done until he has
run his scenario tests. While unit tests assure us that every small piece of JS code is
working as intended, we also want to ensure that the template loads, that it is hooked
up correctly to the controllers, and that clicking around in the template does the right
thing.
The Tests | 99
This is exactly what a scenario test in AngularJS does for you. It allows you to:
So how would a scenario test for our “list of recipes” page work? Well, first of all, before
we get started on the actual test, we need to do some groundwork.
For the scenario test to work, we will need a working web server that is ready to accept
requests from the GutHub application, and will allow storing and getting a list of recipes
from it. Feel free to change the code to use an in-memory list of recipes (removing the
recipe $resource and changing it to just a JSON object dump), or to reuse and modify
the web server we showed you in the previous chapter, or to use Yeoman!
Once we have a server up and running, and serving our application, we can then write
and run the following test:
describe('GutHub App', function() {
it('should show a list of recipes', function() {
browser().navigateTo('/index.html');
// Our Default GutHub recipes list has two recipes
expect(repeater('.recipes li').count()).toEqual(2);
});
});
Up to this point, we have mostly seen how your AngularJS application should be laid
out, how the different AngularJS pieces fit together and work, and a bit on how tem‐
plating in AngularJS works. Together, this allows you to build some sleek, sexy apps,
but they are restricted mostly to the client side. We saw a little bit of the server-side
communication with the $http service back in Chapter 2, but in this chapter, we’ll dig
a little bit deeper into how you would use it in a real-world application.
In this chapter, we will talk about how AngularJS allows you to communicate with your
server, both at the lowest levels of abstraction and with the nice wrappers that it provides.
Furthermore, we will go into how AngularJS can help you speed up your application
with its built-in caching mechanism. If you want to develop a realtime application with
AngularJS using SocketIO, there is an example in Chapter 8 of a possible way to wrap
SocketIO as a directive and use it, so we won’t cover that here.
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readystate == 4 && xmlhttp.status == 200) {
var response = xmlhttp.responseText;
} else if (xmlhttp.status == 400) { // or really anything in the 4 series
// Handle error gracefully
}
};
101
// Setup connection
xmlhttp.open(“GET”, “http://myserver/api”, true);
• GET
• HEAD
• POST
• DELETE
• PUT
• JSONP
In such cases, you can configure your request further through the optional configuration
object passed to the requests. In the prior example, we used the config object to specify
optional URL parameters. But even the GET and POST methods we showed there are
convenience methods. The barebones method call would look something like:
$http(config)
What follows is a basic pseudo-code template for calling this method:
$http({
method: string,
url: string,
params: object,
data: string or object,
headers: object,
transformRequest: function transform(data, headersGetter) or
an array of functions,
transformResponse: function transform(data, headersGetter) or
an array of functions,
cache: boolean or Cache object,
timeout: number,
withCredentials: boolean
});
If there are any special headers you want to set, there are two ways of doing so.
Caching Responses
AngularJS provides a simple caching system for your HTTP GET requests out of the
box. It comes disabled for all requests by default, but to enable caching for your requests,
all you need to do is:
$http.get('http://server/myapi', {
cache: true
}).success(function() { // Handle success });
This enables the cache, and AngularJS stores the response from the server. The next
time a request is made for the same URL, AngularJS returns the response from the cache.
The cache is also smart, so even if you make multiple simultaneous requests for the
same URL, only one request is made to the server and the response is used to fulfill all
the requests.
However, this might be jarring from a usability standpoint, as a user would first see the
old results, then the new results would suddenly show up. For example, a user might be
about to click on an item, and it might change under him.
module.config(function ($httpProvider) {
$httpProvider.defaults.transformRequest = function(data) {
// We are using jQuery’s param method to convert our
// JSON data into the string form
return $.param(data);
};
});
• NamesListCtrl can find all its dependencies (and get them injected correctly).
• The controller makes the request to fetch the names from the server as soon as it
loads.
• The controller correctly saves the response to the names variable on the scope.
While we could construct a controller in our test, and inject a scope and fake HTTP
service into it, let us instead construct the test the same way AngularJS would in its
production code. This is the recommended way, despite it looking a bit more compli‐
cated. Let’s take a look:
describe('NamesListCtrl', function(){
var scope, ctrl, mockBackend;
1. A GET request to /user/123/card returns a list of credit cards for User 123.
2. A GET request to /user/123/card/15 returns the credit card with ID 15 for User 123.
3. A POST request to /user/123/card with credit card info in the POST data creates a
new credit card for User 123.
4. A POST request to /user/123/card/15 with credit card info in the POST data updates
the credit card for User 123 with ID 15.
5. A DELETE request to /user/123/card/15 deletes the credit card with ID 15 for User
123.
In addition to providing objects that allow you to query the server as per your require‐
ments, $resource also allows you to work with the returned objects as if they were
persisted data models, make changes, and ask them to be persisted.
The ngResource is a separate, optional module. To use it, you need to:
Before we look at how we would use the ngResource method of creating a resource, let
us take a look at what it would take to create something similar using the base $http
service. For our credit card resource, we want to be able to get, query, and save credit
cards, in addition to being able to “charge” a credit card.
Here’s one possible implementation:
myAppModule.factory('CreditCard', ['$http', function($http) {
var baseUrl = '/user/123/card';
return {
get: function(cardId) {
return $http.get(baseUrl + '/' + cardId);
},
save: function(card) {
var url = card.id ? baseUrl + '/' + card.id : baseUrl;
return $http.post(url, card);
Now, whenever we ask for a CreditCard from the AngularJS injector, we get an Angular
resource, which by default gives us a few methods to start off with. Table 5-1 lists what
the methods are, and how they behave, so you know how the server should be config‐
ured.
Table 5-1. A credit card resource
Resource Function Method URL Expected Return
CreditCard.get({id: 11}) GET /user/123/card/11 Single JSON
CreditCard.save({}, ccard) POST /user/123/card with post data “ccard” Single JSON
CreditCard.save({id: 11}, ccard) POST /user/123/card/11 with post data “ccard” Single JSON
CreditCard.query() GET /user/123/card JSON Array
CreditCard.remove({id: 11}) DELETE /user/123/card/11 Single JSON
CreditCard.delete({id: 11}) DELETE /user/123/card/11 Single JSON
Let’s take the example of a credit card, which should make things clearer.
// Let us assume that the CreditCard service is injected here
// We can retrieve a collection from the server which makes the request
// GET: /user/123/card
var cards = CreditCard.query();
// We can get a single card, and work with it from the callback as well
CreditCard.get({cardId: 456}, function(card) {
// each item is an instance of CreditCard
expect(card instanceof CreditCard).toEqual(true);
card.name = "J. Smith";
// non-GET methods are mapped onto the instances
card.$save();
The Declaration
Declaring your own $resource is as simple as calling the injected $resource function
(you know how to inject things by now, right?) with the right parameters.
The $resource function takes one required argument—the URL at which the resource
is available—and two optional arguments: default parameters and additional actions
you want to configure on the resource.
Notice that the URL parameter is parametrized (the : denotes a parameter. The :user
Id states that the userId parameter will replace the text there, and the :cardId will be
replaced by the value of the cardId parameter). If the parameter is not passed, then it
will be replaced by an empty string.
The second parameter takes care of the default parameters to be passed along with each
request. In this case, we pass in the userId as a constant 123. The cardId parameter is
more interesting. We say cardId is "@id.” This denotes that if I am using a returned
object from the server, and I call any method on it (such as $save), then the cardId field
is to be picked from the id property on the object.
The third argument is other methods we would like to expose on our custom resource.
We’ll cover this in depth in the following section.
Custom Methods
The third argument to the $resource call is optional additional methods you want to
expose on your resource.
In this case, we specify a method charge. This can be configured by passing in an object,
with the key being the method name to be exposed. The configuration needs to specify
the method type of the request (GET, POST, and so on), the parameters that need to be
passed as part of that request (charge=true in this case), and if the returned result is an
array or not (not, in this case). Once that is done, you are free to start calling Credit
Card.charge() whenever you want (as long as the user has charged in real life, of
course!).
ctrl.fetchAllCards();
This test should look extremely similar to the simple $http unit test, except for one
minor difference. Notice how in our expectation, instead of using the simple “equals”
method, we are using a special toEqualData call. This expectation is smart enough to
ignore the additional methods that the ngResource adds to an object.
While most deferred/Q implementations follow this kind of approach, AngularJS’ im‐
plementation is special for the following reasons:
You might ask the question: why would you want to do something so crazy? Let’s take
a look at a standard problem you might run into with asynchronous functions:
fetchUser(function(user) {
fetchUserPermissions(user, function(permissions) {
fetchUserListData(user, permissions, function(list) {
// Do something with the list of data that you want to display
});
});
});
This is the dreaded pyramid of doom that people complain about when working with
JavaScript. The asynchronous nature of returns competes with the synchronous needs
of the program, leading to multiple nested functions, making it that much harder to
keep track of the current context.
In addition, there is also the matter of error handling. What is the best way to handle
errors? Do you do it in each step? That also gets messy.
To fix this, the Promise proposal offers the concept of then, which takes the functions
to execute in case of a success, on one hand, and error on the other, each of which can
also be chained. So the preceding example, with the Promise API (AngularJS’ imple‐
mentation, at least), could be flattened to:
var deferred = $q.defer();
deferred.promise.then(fetchUser)
.then(fetchUserPermissions)
.then(fetchUserListData)
.then(function(list) {
// Do something with the list of data
}, function(errorReason) {
Response Interception
We have covered making calls to the server, handling responses, wrapping the responses
nicely in abstractions, and dealing with asynchronous calls. But in any real world ap‐
plication, you would end up having to do some common operations for each server call
you made, tasks such as error handling, authentication, and other security considera‐
tions like pruning the data.
With a solid understanding of the $q API, we can now set about doing all of the above
using Response Interceptors. Response Interceptors allow you (as the name would
suggest) to intercept responses before they make it to the application, and apply your
transformations, error handling, and everything else, including the kitchen sink.
Let us take an example which intercepts the responses and does some minor data trans‐
formation.
// register the interceptor as a service
myModule.factory('myInterceptor', function($q, notifyService, errorLog) {
return function(promise) {
return promise.then(function(response) {
// Do nothing
return response;
}, function(response) {
// My notify service updates the UI with the error message
notifyService(response);
// Also log it in the console for debug purposes
errorLog(response);
return $q.reject(response);
});
}
});
Security Considerations
Now, when working with web applications, security is a huge concern and should be
kept at the top of one’s mind. AngularJS does provide some assistance when it comes
to two common attack vectors, which we will cover in the following sections.
XSRF
XSRF (Cross-Site Request Forgery) attacks usually have the following characteristics:
<img src="http://www.examplebank.com/xfer?from=UserA&amount=10000&to=UserB">
If User A’s bank keeps the authentication information in a cookie, and it hasn’t expired,
then when User A opens User B’s website, it would trigger an unauthorized transfer
from User A to User B.
So how does AngularJS help prevent this? It provides a two-step mechanism to prevent
XSRF vulnerabilities.
On the client side, when performing XHR requests, the $http service reads a token from
a cookie called XSRF-TOKEN and sets it as an HTTP header X-XSRF-TOKEN. Since
only your requests from your domain could have read and set the token, you can be
assured that the XHR came from your domain.
This also requires a slight modification of your server code, so that it sets a readable
session cookie called XSRF-TOKEN on the first HTTP GET request. Subsequent re‐
quests to the server can verify that the value in the HTTP header matches the XSRF
token set in the first request. Of course the token must be unique to every user, and
must be verifiable by the server (to prevent the JavaScript from making up its own
tokens).
With directives, you can extend HTML to add declarative syntax to do whatever you
like. By doing so, you can replace generic <div>s and <span>s with elements and at‐
tributes that actually mean something specific to your application. The ones that come
with Angular provide basic functionality, but you can create your own to do things
specific to your application.
First we’re going to go over the directives API and how it fits within the Angular startup
and runtime lifecycles. From there, we’ll use this knowledge to create several classes of
directives. We’ll finish the chapter with how to write unit tests for directives and how
to make these run quickly.
But first, a few notes on the syntax for using directives.
119
Table 6-1. HTML Validation Schemes
Validator Format Example
none namespace-name ng-repeat=item in items
XML namespace:name ng:repeat=item in items
HTML5 data-namespace-name data-ng-repeat=item in items
xHTML x-namespace-name x-ng-repeat=item in items
Because you can use any of these, the Angular documentation lists directives with a
camel-case format, instead of any of these options. For example, ng-repeat is found
under the title ngRepeat. As you’ll see in a bit, you’ll use this naming format when
defining your own directives.
If you don’t use an HTML validator (most folks don’t), you’ll be just fine using the
namespace-directive syntax as you’ve seen in the examples so far.
API Overview
A basic pseudo-code template for creating any directive follows:
var myModule = angular.module(...);
restrict
The restrict property lets you specify the declaration style for your directive—that is,
whether it can be used as an element name, attribute, class, or comment. You can specify
one or more declaration styles using a character to represent each of them from the set
in Table 6-3:
Table 6-3. Options for directive declaration usage
Character Declaration style Example
E element <my-menu title=Products></my-menu>
A attribute <div my-menu=Products></my-menu>
C class <div class=my-menu:Products></div>
M comment <!-- directive: my-menu Products -->
If you wanted to use your directive as either an element or an attribute, you’d pass EA
as the restrict string.
If you omit the restrict property, the default is A, and your directive can be used only
as an attribute.
If you plan to support IE8, attribute- and class-based directives are your best bet, as it
requires extra effort to make new elements work properly. See the Angular documen‐
tation for full details on this.
Priorities
In cases where you have multiple directives on a single DOM element and where the
order in which they’re applied matters, you can use the priority property to order their
application. Higher numbers run first. The default priority is 0 if you don’t specify one.
Needing to set priority will likely be a rare occurrence. One example of a directive that
needs to set priority is the ng-repeat. When repeating elements, we want Angular to
make copies of the template element before other directives get applied. Without this,
the other directives would get applied to the canonical template element rather than to
the repeated elements we want in our app.
Though it’s not in the documentation, you can search the Angular source for the few
other directives that use priority. For ng-repeat, we use a priority value of 1000, so
there’s plenty of room for other priorities beneath it.
Instead of having a bunch of <div>, <ul><li>, and <a> elements, you could create the
directives <tab-set> and <tab>, which declare the structure of each tab respectively.
Your HTML then does a much better job of expressing the intent of your template. The
end result could look like:
<tab-set>
<tab title='Home'>
<p>Welcome home!</p>
</tab>
<tab title='Preferences'>
<!-- preferences UI goes here -->
</tab>
</tabset>
You could also data bind the strings for title and the tab content via a controller on <tab>
or <tabset>. And it’s not limited to tabs—you can do menus, accordions, pop-ups,
dialog boxes, or anything else your app needs in this way.
You specify the replacement DOM elements either through the template or the tem
plateUrl properties. You’d use template to set the template content via a string, and
templateUrl to refer to the template to be loaded from a file on the server. As you’ll see
in the following example, you can pre-cache these templates to reduce the number of
GET requests, potentially improve performance.
Let’s write a dumb directive: a <hello> element that just replaces itself with <div>Hi
there</div>. In it, we’ll set restrict to allow elements and set template to what we
want to display. As the default behavior is to append content to elements, we’ll set
replace to true to replace the original template:
Loading these files through templateUrl will, however, make your user wait until they
load to see the directive. If you want to have the template load with the first page, you
can include it as part of the page in a script tag, like so:
<script type='text/ng-template' id='helloTemplateInline.html'>
<div>Hi there</div>
</script>
The id attribute here is important, as this is the URL key that Angular uses to store the
template. You’ll use this id later in your directive’s templateUrl to specify which tem‐
plate to insert.
This version will load just fine without a server, as no XMLHttpRequest is necessary to
fetch the content.
Finally, you could load the templates yourself over $http or another mechanism and
then set them directly in the object Angular uses called the $templateCache. We want
this template available in the cache before the directives run, so we’ll call it via a run
function on our module.
var appModule = angular.module('app', []);
appModule.run(function($templateCache) {
$templateCache.put('helloTemplateCached.html', '<div>Hi there</div>');
});
appModule.directive('hello', function() {
return {
restrict: 'E',
templateUrl: 'helloTemplateCached.html',
replace: true
};
});
You would likely want to do this in production only as a technique to reduce the number
of GET requests required. You’d run a script to concatenate all the templates into a single
file, and load it in a new module that you then reference from your main application
module.
Notice that one difference here is that the link function gets access to a scope but
compile does not. This is because during the compile phase, the scope doesn’t exist yet.
You do, however, have the ability to return link functions from the compile function.
These link functions do have access to the scope.
Notice also that both compile and link receive a reference to their DOM element and
the list of attributes for that element. The difference here is that the compile function
receives the template element and attributes from the template, and thus gets the t
prefix. The link function receives them from the view instances created from the tem‐
plate, and thus gets the i prefix.
This distinction only matters when the directive is within some other directive that
makes copies of the template. The ng-repeat directive is a good example.
<div ng-repeat='thing in things'>
<my-widget config='thing'></my-widget>
</div>
Here, the compile function will be called exactly once, but the link function will be
called once per copy of my-widget—equal to the number of elements in things. So, if
Scopes
You will often want to access a scope from your directive to watch model values and
make UI updates when they change, and to notify Angular when external events cause
the model to change. This is most common when you’re wrapping some non-Angular
component from jQuery, Closure, or another library, or implementing simple DOM
events. Evaluate Angular expressions passed into your directive as attributes.
When you want a scope for one of these reasons, you have three options for the type of
scope you’ll get:
You can create these scope configurations with the following syntax:
Scope Type Syntax
existing scope scope: false (this is the default if unspecified)
new scope scope: true
isolate scope scope: { /* attribute names and binding style */ }
These are fairly abstract concepts, so let’s look at some variations on a concrete example
to illustrate. Let’s say that we want to create an expander directive that shows a title bar
that expands to display extra content when clicked.
It would look like Figure 6-2 when closed.
If we think it would make more sense to define the expander title in the template rather
than in the model, we can use the string-style attribute passing denoted by an @ symbol
in the scope declaration, like this:
scope: { title:'@expanderTitle' },
In the template we can achieve the same effect with:
<expander class='expander' expander-title='Click me to expand'>
{{text}}
</expander>
Note that with this @ strategy we could still data bind the title to our controller’s scope
by using interpolation :
<expander class='expander' expander-title='{{title}}'>
{{text}}
</expander>
As an example, let’s re-implement the previous expander example without the help of
ng-show and ng-click. It would look like the following:
angular.module('expanderModule', [])
.directive('expander', function(){
return {
restrict: 'EA',
replace: true,
transclude: true,
scope: { title:'=expanderTitle' },
template: '<div>' +
'<div class="title">{{title}}</div>' +
'<div class="body closed" ng-transclude></div>' +
titleElement.bind('click', toggle);
function toggle() {
bodyElement.toggleClass('closed');
}
}
}
});
We’ve removed the ng-click and ng-show directives from the template. Instead, to
perform the desired action when users click on the expander title, we’ll create a jqLite
element from the title element and bind the click event to it with a toggle() function
as its callback. In toggle(), we’ll call toggleClass() on the expander body element to
add or remove a class called closed, where we’d set the element to display: none with
a class like this:
.closed {
display: none;
}
Controllers
When you have nested directives that need to communicate with each other, the way to
do this is through a controller. A <menu> may need to know about the <menu-item>
elements inside it so it can show and hide them appropriately. The same would be true
for a <tab-set> knowing about its <tab> elements, or a <grid-view> knowing about
its <grid-element> elements.
As previously shown, to create an API to communicate between directives, you can
declare a controller as part of a directive with the controller property syntax:
controller: function controllerConstructor($scope, $element, $attrs, $transclude)
This controller function is dependency injected, so the parameters listed here, while
potentially useful, are all optional—they can be listed in any order. They’re also only a
subset of the services available.
Other directives can have this controller passed to them with the require property
syntax. The full form of require looks like:
require: '^?directiveName'
As an example, let’s rewrite our expander directive to be used in a set called “accordion,”
which ensures that when you open one expander, the others in the set automatically
close. This looks something like Figure 6-4.
First, let’s write the accordion directive that will do the coordination. We’ll add our
controller constructor here with methods to do the coordination:
appModule.directive('accordion', function() {
return {
restrict: 'EA',
replace: true,
transclude: true,
template: '<div ng-transclude></div>',
controller: function() {
var expanders = [];
this.addExpander = function(expander) {
expanders.push(expander);
}
}
}
});
Moving On
As we’ve seen, directives let us extend HTML’s syntax and turn many application tasks
into a do-what-I-mean declaration. Directives make reuse a breeze—from configuring
your app, like with ng-model and ng-controller, to doing template tasks like ng-
repeat and ng-view, to sky’s-the-limit reusable components such as data-grids, bubble-
charts, tool-tips, and tabs.
In this chapter, we will take a look at some other useful features that are present in
AngularJS, but weren’t covered at all or in depth in the chapters and examples so far.
$location
Up to now, you have seen quite a few examples of the $location service in AngularJS.
Most of them would have been fleeting glances—an access here, set there. In this section,
we will dive into what exactly the $location service in AngularJS is for, and when you
should and shouldn’t use it.
The $location service is a wrapper around the window.location that is present in any
browser. So why would you want to use it instead of working directly with window.lo
cation?
Goodbye global state
window.location is a prime example of global state (actually, both window and
document objects in the browser are prime examples). The minute you have global
state in your application, testing, maintaining and working with it becomes a hassle
(if not now, then definitely in the long run). The $location service hides this nas‐
tiness (what we call global state), and allows you to test the browser’s location details
by injecting mocks during your unit tests.
API
window.location gives you total access to the contents of the browser location.
That is, window.location gives you the string while $location gives you nice,
jQuery-like setters and getters to work with it in a clean way.
137
AngularJS integration
If you use $location, you can use it however you want. But with window.loca
tion, you would have to be responsible for notifying AngularJS of changes, and
listen to changes as well.
HTML5 integration
The $location service is smart enough to realize when HTML5 APIs are available
within a browser and use them. If they’re not available, it will fall back to the default
usage.
So when should you use the $location service? Any time you want to react to a change
in the URL (that is not covered by the $routes, which you should primarily use for
working with URL-based views), as well as effect a change in the current URL in the
browser.
Let’s consider a small example of how you would use the $location service in a real-
world application. Consider a case where we have a datepicker, and when a date is
selected, the app navigates to a certain URL. Let us take a look at how that might look:
// Assume that the datepicker calls $scope.dateSelected with the date
$scope.dateSelected = function(dateTxt) {
$location.path('/filteredResults?startDate=' + dateTxt);
// If this were being done in the callback for
// an external library, like jQuery, then we would have to
$scope.$apply();
};
While both of these will have the same effect, they differ in one significant way.
The first will capture any errors that happen when executeSomeAction is called, while
the latter will quietly ignore any such errors. You will get error notifications from An‐
gularJS only when you do the first.
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
You can monkey patch this into the topmost scope or the rootscope, and then use the
$scope.$safeApply function everywhere. This has been under discussion, and hope‐
fully in a future release, this will be the default behavior.
$location | 139
What are those other methods also available on the $location object? Table 7-1 contains
a quick summary for you to use in a bind.
Let us take a look at how the $location service would behave, if the URL in the browser
was http://www.host.com/base/index.html#!/path?param1=value1#hashValue.
Table 7-1. Functions on the $location service
Getter Function Getter Value Setter Function
absUrl() http://www.host.com/base/index.html#!/path?param1=value1#hashValue N/A
hash() hashValue hash('newHash')
host() www.host.com N/A
path() /path path('/newPath')
protocol() http N/A
search() {'a’: ‘b'} search({'c’: ‘def'})
url() /path?param1=value1?hashValue url('/newPath?p2=v2')
The Setter Function column in Table 7-1 has some sample values that denote the type
of object the setter function expects.
Note that the search() setter has a few modes of operation:
Using any one of the setters does not mean that window.location will get changed in‐
stantly! The $location service plays well with the Angular lifecycle, so all changes to
the location will accumulate and get applied together at the end of the cycle. So feel free
to make those changes, one after the other, without fear that the user will see a URL that
keeps flickering and changing underneath him.
$location | 141
AngularJS will kick in from that point onward and take care of things. It will detect
changes to the path and redirect to the correct AngularJS routes that were defined.
Link rewriting
You can easily specify URLs as follows:
<a href="/some?foo=bar">link</a>
Depending on whether you are using HTML5 mode or not, AngularJS will take
care to redirect to /some?foo=bar or index.html#!/some?foo=bar, respectively. No
additional steps are required on your part. Awesome, isn’t it?
But the following types of links will not be rewritten, and the browser will perform
a full reload on the page:
a. Links that contain a target element such as the following:
<a href="/some/link" target="_self">link</a>
b. Absolute links going to a different domain:
<a href="http://www.angularjs.org">link</a>
This is different because it is an absolute URL, while the previous example used
the existing base URL.
c. Links starting with a different base path when one is already defined:
<a href="/some-other-base/link">link</a>
Relative Links
Be sure to check all relative links, images, scripts, and so on. You must either specify
the URL base in the head of your main HTML file (<base href="/my-base">), or
you must use absolute URLs (starting with /) everywhere because relative URLs will
be resolved to absolute URLs using the initial absolute URL of the document, which
is often different from the root of the application.
Running Angular apps with the History API enabled from document root is
strongly encouraged, as it takes care of all relative link issues.
1. It is declarative. That means it is written in a way that is easier to write and under‐
stand. It reads like English!
2. It is modular. It forces you to think about how you define your components and
dependencies, and makes them explicit.
3. It allows for easy testing. In your unit tests, you can selectively pull in modules, and
avoid the untestable portions of your code. And in your scenario tests, you can load
additional modules, which can make working with some components easier.
Let us first take a look at how you would use a module that you have defined, then take
a look at how we would declare one.
Say we have a module, in fact, the module for our application, called “MyAwesomeApp.”
In my HTML, I could just add the following to the <html> tag (or technically, any other
tag):
<html ng-app="MyAwesomeApp">
The ng-app directive tells AngularJS to bootstrap your application using the MyAwe‐
someApp module.
So how would that module be defined? Well, for one, we recommend that you have
separate modules for your services, directives, and filters. Your main module could then
just declare the other modules as a dependency (just like we did in Chapter 4 with the
RequireJS example).
This makes it easier to manage your modules, as they are nice complete chunks of code.
Each module has one and only one responsibility. This also allows your tests to load
only the modules they care about, and thus reduces the amount of initialization that
needs to happen. The tests can be small and focused.
Convenience Methods
What can you do with a module? We can instantiate controllers, directives, filters, and
services, but the module class allows you to do more, as you can see in Table 7-2:
Table 7-2. Module configuration methods
API Method Description
config(configFn) Use this method to register work that needs to be done when the module is
loading.
constant(name, object) This happens first, so you can declare all your constants app-wide, and have
them available at all configuration (the first method in this list) and instance
methods (all methods from here on, like controller, service, and so on).
controller(name, constructor) We have seen a lot of examples of this; it basically sets up a controller for use.
directive(name, directiveFactory) As discussed in Chapter 6, this allows you to create directives within your
app.
filter(name, filterFactory) Allows you to create named AngularJS filters, as discussed in previous
chapters.
run(initializationFn) Use this method when you want to perform work that needs to happen once
the injector is set up, right before your application is available to the user.
value(name, object) Allows values to be injected across the application.
service(name, serviceFactory) Covered in the next section.
factory(name, factoryFn) Covered in the next section.
provider(name, providerFn) Covered in the next section.
You might realize that we are missing the details of three particular API calls—Factory,
Provider, and Service—from the preceding table. There is a reason for that: it is quite
easy to confuse the usage between the three, so we will dive into a small example that
should better illustrate when (and how!) to use each one.
The Factory
The Factory API call is used whenever we have a class or object that needs some
amount of logic or parameters before it can be initialized. A Factory is a function
that is responsible for creating a certain value (or object). Let us take the example
of a greeter function that needs to be initialized with its salutation:
function Greeter(salutation) {
this.greet = function(name) {
The Service
What about services? Well, the difference between a Factory and a Service is that
the Factory invokes the function passed to it and returns a result. The Service in‐
vokes “new” on the constructor method passed to it and returns the result.
So the preceding greeter Factory could be replaced with a greeter Service as follows:
myApp.service('greeter', Greeter);
Every time I asked for a greeter, AngularJS would call the new Greeter() and return
that.
The Provider
This is the most complicated (and thus most configurable, obviously) of the lot.
The Provider combines both the Factory and the Service, and also throws in the
benefit of being able to configure the Provider function before the injection system
is fully in place (in the config blocks, that is).
Let’s see how a modified greeter Service using the Provider might look:
myApp.provider('greeter', function() {
var salutation = 'Hello';
this.setSalutation = function(s) {
salutation = s;
}
function Greeter(a) {
this.greet = function() {
return salutation + ' ' + a;
}
}
this.$get = function(a) {
return new Greeter(a);
};
});
This allows us to set the salutation at runtime (for example, based on the language
of the user).
AngularJS would internally call $get whenever someone asked for an instance of the
greeter object.
Warning!
There is a slight, but significant difference between using:
angular.module('MyApp', [...])
and:
angular.module('MyApp')
The difference is that the first creates a new Angular module, pulling in the module
dependencies listed in the square brackets ([…]). The second uses the existing module
that has already been defined by the first call.
So you should make sure that you use the following code only once in your entire
application:
angular.module('MyApp', [...]) // Or MyModule, if you are modularizing your app
If you do not plan to save it to a variable and refer to it across your application, then
use angular.module(MyApp) in the rest of the files to ensure you get a handle to the
correct AngularJS module. Everything on the module must be defined by accessing
the variable, or be added on the spot where the module has been defined.
The additional arguments to $emit are passed on as function parameters to the listener
functions. Also, $emit communicates only upwards from its current scope, so the poor
denizens of the planet (if they had a scope to themselves) would not be notified if their
planet was being destroyed.
Similarly, if a Galaxy wanted to communicate downwards to its child, the Star System
scope, then it could communicate as follows:
scope.$emit('selfDestructSystem', targetSystem);
Then all Star Systems listening for the event could look at the target system, and decide
if they should self-destruct, using these commands:
scope.$on('selfDestructSystem', function(event, targetSystem) {
if (scope.mySystem === targetSystem) {
scope.selfDestruct(); // Go Ka-boom!!
}
});
Of course, as the event propagates all the way up (or down), it might become necessary
at a certain level or scope to say, “Enough! You shall not PASS!” or to prevent what the
event does by default. The event object passed to the listener has functions to handle all
of the above, and more, so let us take a quick look at what you can get up to with the
event object in Table 7-3.
Table 7-3. Event object properties and methods
Property of event Purpose
event.targetScope The scope which emitted or broadcasted the event originally
event.currentScope The scope currently handling the event
event.name The name of the event
event.stopPropagation() A function which will prevent further event propagation (this is available only for events
that were $emitted
event.preventDefault() This actually doesn’t do anything but set defaultPrevented to true. It is up to the
implementer of the listeners to check on defaultPrevented before taking action
event.defaultPrevented true if preventDefault was called
• currency
• date/time
• number
There is also pluralization support (for English as well as i18n/L10n) with the ngPlur
alize directive.
All of this pluralization support is handled and managed by the $locale service, which
manages the locale-specific rule sets. The $locale service works off of locale IDs, which
generally consist of two parts: the country code and the language code. For example,
en_US and en_UK, denote English used in the US and the UK, respectively. Specifying a
country code is optional, just specifying “en” is a valid locale code.
Common Gotchas
Translation Length
You design your UI so that it shows June 24, 1988 in a div that has been painstakingly
sized to fit it just right. And then you open your UI in Spanish. 24 de junio de 1988
just doesn’t fit in that same space anymore…
When internationalizing your apps, keep in mind that the lengths of your strings
might change drastically from language to language. Design your CSS accordingly,
and do thorough testing across the various languages. (Don’t forget that right to left
languages also exist!)
Timezones
The AngularJS date/time filter picks up the timezone settings from the browser. So
depending on the timezone of the computer, different people might see different
information. Neither JS nor AngularJS have any built-in support to display time
with a timezone specified by the developer.
So what happens when we use the ng-bind-html directive on the same myUnsafeHTML
Content, like so?
<div ng-bind-html="myUnsafeHTMLContent"></div>
The output in such a case would be the following:
an html _click here_ snippet
The important things to note are that the style tag (with color blue), and the onmouse
over handler on the <em> tag are both removed by AngularJS. They are deemed unsafe,
and thus dropped.
The color of the text is blue (as per the style attached to the p tag), and the click here
does have an onmouseover registered on it. So the minute your mouse strays anywhere
near the click here text, the output would change to:
an html PWN3D! snippet
As you can see, this is quite unsafe in reality, so be absolutely certain that this is what
you want when you decide to use the ng-bind-html-unsafe directive. Someone could
just as easily read the user’s information and send it to his or her servers.
Linky
The linky filter is also present in the ngSanitize module, and basically allows you to
add it to HTML content that is being rendered and convert links that are present in the
HTML to anchor tags. It is quite simple to use, so let us take a look at an example:
$scope.contents = 'Text with links: http://angularjs.org/ & mailto:us@there.org';
Now, if you use the following binding:
<div ng-bind-html="contents"></div>
this would result in the contents of the HTML getting printed as:
Text with links: http://angularjs.org/ & mailto:us@there.org
Now let’s take a look at what happens when we use the linky filter:
<div ng-bind-html="contents | linky"></div>
The linky filter goes through the text contents and adds <a> tags to all URLs and mailto
links it finds, thus providing HTML content that the user can interact with:
Text with links: http://angularjs.org/ & us@there.org
By now, we have covered pretty much all the different parts of the Angular, including
directives, services, controllers, resources, and so much more. But even we know that
sometimes just reading about it isn’t enough. And sometimes we don’t care about how
any of that works, we just want to know how to do that one thing with AngularJS?
In this chapter, we take a stab at giving complete coding samples (with little bits of info
and pointers to explain what is happening) for some common problems we tackle in
most web apps. They are in no particular order, so feel free to jump to whichever ones
you care about, or go through them in order. You are the boss!
The examples covered in this chapter include:
153
That is, we want to modify the Input field by adding an attribute datepicker, and adding
some more functionality to it (like data binding with the model and the ability to be
notified when a date is selected). So how would we go about it?
We will re-use existing functionality, the jQuery UI’s datepicker, instead of building a
datepicker from scratch. We just need to hook it up to AngularJS and latch onto the
hooks it provides:
angular.module('myApp.directives', [])
.directive('datepicker', function() {
return {
// Enforce the angularJS default of restricting the directive to
// attributes only
restrict: 'A',
// Always use along with an ng-model
require: '?ngModel',
scope: {
// This method needs to be defined and
// passed in to the directive from the view controller
select: '&' // Bind the select function we refer to the
// right scope
},
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;
optionsObj.dateFormat = 'mm/dd/yy';
var updateModel = function(dateTxt) {
scope.$apply(function () {
// Call the internal AngularJS helper to
// update the two-way binding
ngModel.$setViewValue(dateTxt);
});
};
ngModel.$render = function() {
// Use the AngularJS internal 'binding-specific' variable
element.datepicker('setDate', ngModel.$viewValue || '');
};
element.datepicker(optionsObj);
}
ng-model
We get an ng-model attribute passed into the linking function of the directive. The ng-
model (which is mandatory for the directive to function because of the require attribute
inside the directive definition) allows us to define how the attribute and object linked
to the ng-model behave in the context of the directive. There are two things you need
to pay attention to:
ngModel.$setViewValue(dateTxt)
This is called when something external to AngularJS (in this case, the onSelect of
the jQuery UI datepicker) happens. This lets AngularJS know that it has to update
the model. This is usually called when a DOM event happens.
ngModel.$render
This is the other part to the ng-model. This tells Angular how to update the view
when the model changes. In our case, we just pass on to the jQuery UI that the
datepicker value has changed.
Binding select
Instead of using the attribute value and evaluating it as a string against the scope (in
which case, nested functions and objects won’t be accessible), we use function binding
(the “&” scope binding). This creates a new function on the scope called select, which
takes one argument—an object. Each key in this object must match an argument speci‐
fied in the HTML where the directive is used. The value for that key will be the value
passed to the function as that argument. The added advantage is that this decouples the
controller implementation from having to know anything about the DOM or the di‐
rective. The callback function just performs its behavior given certain arguments, and
does not need to know about the binding or the updates.
Calling select
Notice that we pass in an optionsObj to the datepicker, with an onSelect function.
jQuery UI is responsible for calling the onSelect function, which will usually happen
outside of AngularJS’ execution context. Of course, when functions like onSelect are
called, AngularJS has no clue. It is up to us to let AngularJS know that it needs to act on
stuff. How do we do that? By using scope.$apply.
app.controller('MainCtrl', function($scope) {
$scope.myText = 'Not Selected';
$scope.currentDate = '';
$scope.updateMyText = function(date) {
$scope.myText = 'Selected';
};
});
Pretty simple stuff. We declare a controller, set some scope variables, and then create a
scope method (updateMyText) that we will later use for binding to the on-select event
of the datepicker. On to the HTML next:
<!DOCTYPE html>
<html ng-app="myApp">
<head lang="en">
<meta charset="utf-8">
<title>AngularJS Datepicker</title>
<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
</script>
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js">
</script>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/
angular.min.js">
</script>
<link rel="stylesheet"
href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css">
<script src="datepicker.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<input id="dateField"
datepicker
ng-model="$parent.currentDate"
select="updateMyText(date)">
Notice how the select attribute is specified. There is no value “date” on the scope. But
because of the way we have set up our function binding in the directive, AngularJS now
knows that the function will take an argument, whose name will be “date.” This is what
we specified as an object when the onSelect of the datepicker is called.
For the ng-model, we specify $parent.currentDate instead of currentDate. Why? Because
our directive creates an isolated scope so that we can have the select function bound.
This makes it so that the currentDate is no longer linked by ng-model even if we set it.
So we have to explicitly tell AngularJS that the currentDate it needs to refer to is not
in the isolated scope, but in its parent scope.
With this, when you load it up in your browser, you would see a text box that, when
clicked, exposes the jQuery UI datepicker. On select, it updates the text on the screen
from “Not Selected” to “Selected,” and your date. The date in the input field is also
updated.
The app itself is quite simple. There is data, which is a list of teams from various sport,
such as basketball, football (the NFL kind, not the soccer kind), and hockey. For each
of these teams, we have the name, the city, the sport, and whether the team is featured
or not.
What we want to do is display this list, and also display filters on the left that immediately
update the list as you modify them. We are going to have two controllers: one for storing
the data, and the other to work with the filters. And we are going to use a service to
communicate the changes to the filter between the ListCtrl and the FilterCtrl.
Let us take a look at the service first, which is going to drive the application:
angular.module('myApp.services', []).
factory('filterService', function() {
<head lang="en">
<meta charset="utf-8">
<title>Teams List App</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
</script>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js">
</script>
<link rel="stylesheet"
href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/
css/bootstrap.min.css">
<script
src="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.1.1/
bootstrap.min.js">
</script>
<script src="services.js"></script>
<script src="app.js"></script>
</head>
<body>
<div class="row-fluid">
<div class="span3" ng-controller="FilterCtrl">
<form class="form-horizontal">
<div class="controls-row">
<label for="searchTextBox" class="control-label">Search:</label>
<div class="controls">
<input type="text"
id="searchTextBox"
ng-model="filterService.searchText">
</div>
</div>
<div class="controls-row">
<label for="sportComboBox" class="control-label">Sport:</label>
<div class="controls">
<select id="sportComboBox"
ng-model="filterService.activeFilters.sport">
<option ng-repeat="sport in ['Basketball', 'Hockey', 'Football']">
{{sport}}
</option>
</select>
<div class="controls-row">
<label for="cityComboBox" class="control-label">City:</label>
<div class="controls">
<select id="cityComboBox"
ng-model="filterService.activeFilters.city">
<option ng-repeat="city in ['Dallas', 'Los Angeles',
'Boston', 'New York']">
{{city}}
</option>
</select>
</div>
</div>
<div class="controls-row">
<label class="control-label">Featured:</label>
<div class="controls">
<input type="checkbox"
ng-model="filterService.activeFilters.featured"
ng-false-value="" />
</div>
</div>
</form>
</div>
<div class="offset1 span8" ng-controller="ListCtrl">
<table>
<thead>
<tr>
<th>Name</th>
<th>Sport</th>
<th>City</th>
<th>Featured</th>
</tr>
</thead>
<tbody id="teamListTable">
<tr ng-repeat="team in teamsList | filter:filterService.activeFilters |
filter:filterService.searchText">
<td>{{team.name}}</td>
<td>{{team.sport}}</td>
<td>{{team.city}}</td>
<td>{{team.featured}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
The Repeater
Let us take a look at the ng-repeat statement one more time:
"team in teamsList | filter:filterService.activeFilters |
filter:filterService.searchText"
The first part is the same as always. It is the two new filters that make all the difference.
The first filter tells AngularJS to filter the list using filterService.activeFilters.
This basically takes each property in the object filters and ensures that each item in the
repeater matches corresponding properties in the filter. So if activeFilters[city] =
Dallas, then only those items in the repeater with city = Dallas will be selected. If there
are multiple filters, then all of them would have to match.
The second filter is a textual match filter. It basically selects only those items that have
the value of filterService.searchText present in any of their values. So it will do a
match across cities, team names, sports, and featured.
if (scope.done) {
optionsObj.done = function() {
scope.$apply(function() {
scope.done({e: e, data: data});
});
};
}
if (scope.progress) {
optionsObj.progress = function(e, data) {
scope.$apply(function() {
scope.progress({e: e, data: data});
});
}
}
element.fileupload(optionsObj);
}
};
});
<head lang="en">
<meta charset="utf-8">
<title>File Upload with AngularJS</title>
<!-- Because we are loading jQuery before AngularJS,
Angular will use the jQuery library instead of
its own jQueryLite implementation -->
<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
</script>
<script
src="http://raw.github.com/blueimp/jQuery-File-Upload/master/js/vendor/
jquery.ui.widget.js">
</script>
<script
src="http://raw.github.com/blueimp/jQuery-File-Upload/master/js/
jquery.iframe-transport.js">
</script>
<script
src="http://raw.github.com/blueimp/jQuery-File-Upload/master/js/
jquery.fileupload.js">
</script>
<script
src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js">
</script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
File Upload:
<!-- We will define uploadFinished in MainCtrl and attach
it to the scope, so that it is available here -->
<input id="testUpload"
type="file"
fileupload
name="files[]"
</html>
app.controller('MainCtrl', function($scope) {
$scope.uploadFinished = function(e, data) {
console.log('We just finished uploading this baby...');
};
});
And with that, we have a simple, working, reusable file upload directive.
Using Socket.IO
A common requirement for the web nowadays is real-time web applications, which
need to be updated as soon as the data on the server is updated. Previously used tech‐
niques such as polling have been found lacking, and sometimes we just want to open a
socket to our client and communicate.
Socket.IO is a brilliant library that allows you to do just that, and uses a dead simple,
event-based API to allow you to develop real-time web apps. We are going to develop
• Socket.IO events are noticed and handled within the AngularJS lifecycle
• It becomes easy to test the integration later
We are just wrapping the two functions we care about, which are the on event and
broadcast event methods of the Socket.IO API. There are a bunch more, and they can
be wrapped in a similar manner.
We are going to have a simple index.html, which shows a textbox with a send button
and a list of messages. In this example, we do not keep track of who sends the messages
or when they are sent.
<head lang="en">
<meta charset="utf-8">
<title>Anonymous Broadcaster</title>
<script src="/socket.io/socket.io.js">
</script>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js">
</script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<input type="text" ng-model="message">
<button ng-click="broadcast()">Broadcast</button>
<ul>
<li ng-repeat="msg in messages">{{msg}}</li>
</ul>
</body>
</html>
Let’s go to our MainCtrl (this is part of app.js), which is where we pull this all together:
function MainCtrl($scope, socket) {
$scope.message = '';
$scope.messages = [];
server.listen(8080);
socket.on('broadcast:msg', function(data) {
// Tell all the other clients (except self) about the new message
socket.broadcast.emit('new:msg', data.message);
});
});
You could easily expand this later to handle more messages and more intricate struc‐
tures, but this example lays the foundation on which you can implement socket con‐
nections between your client and server.
The app is very simple. It does not do any validation (whether the messages are empty),
but it does have the HTML sanitization that AngularJS provides by default. It does not
handle complex messages, but it does offer a fully working end-to-end Socket.IO im‐
plementation integrated into AngularJS that you can now build your work off of.
The Paginator service expects two arguments when it is called: a fetch function, and
the size of each page. The fetch function expects the following signature:
fetchFunction(offset, limit, callback);
It will then be called with the correct offset and limit by the Paginator whenever it needs
to fetch and display a certain page. Internal to the function, it can either slice the data
beforeEach(module('services'));
var paginator;
beforeEach(inject(function(Paginator) {
paginator = Paginator(fetchFn, 3);
}));
The Paginator exposes currentPageItems on itself, which can then be bound from the
templates on a repeater (or however else you want to display them). The hasNext()
and hasPrevious() can be used to figure out when to show the Next and Previous Page
links, and on click, it just needs to call next() or previous(), respectively.
How would you use this with something that needs to fetch data from the server for
each page? Here is what a possible controller that fetches search results from the server
one page at a time could look like:
var app = angular.module('myApp', ['myApp.services']);
<head lang="en">
<meta charset="utf-8">
<title>Pagination Service</title>
<script
src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js">
</script>
<script src="pagination.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<input type="text" ng-model="query">
<ul>
<li ng-repeat="item in searchPaginator.currentPageItems">
{{item}}
</li>
</ul>
<a href=""
ng-click="searchPaginator.previous()"
</html>
So in this (slightly contrived) example, we will have the skeleton of a full-fledged app
that will:
1. Show all unrecoverable errors (Non 401s) in a butterbar directive that gets shown
on all screens only when an error exists.
2. Have an ErrorService which will be used for communicating between the direc‐
tive, the view, and the controllers.
3. Fire an event (event:loginRequired) whenever the server responds with a 401.
This will then get handled by a root controller that oversees the entire application.
4. Handle requests that need to be made to the server with some authorization headers
that are specific to the current user.
We will not go over the entire application (the routes, the templates, and so on), as most
of those are fairly straightforward. We will highlight only the pieces that are relevant (so
you can copy and paste that into your codebase and get started right away). These will
be fully functional. If you want to revisit defining Services and Factories, jump to
Chapter 7. If you want to take a look at how to work with servers, you can refer to
Chapter 5.
Let us first take a look at the Error service:
var servicesModule = angular.module('myApp.services', []);
servicesModule.factory('errorService', function() {
Our error message directive, which is actually independent of the Error service, would
just look for an alert message attribute, and then bind to it. It would conditionally show
itself when the alert message is present.
// USAGE: <div alert-bar alertMessage="myMessageVar"></div>
angular.module('myApp.directives', []).
directive('alertBar', ['$parse', function($parse) {
return {
restrict: 'A',
template: '<div class="alert alert-error alert-bar"' +
'ng-show="errorMessage">' +
'<button type="button" class="close" ng-click="hideAlert()">' +
'x</button>' +
'{{errorMessage}}</div>',
scope.$watch(alertMessageAttr, function(newVal) {
scope.errorMessage = newVal;
});
scope.hideAlert = function() {
scope.errorMessage = null;
// Also clear the error message on the bound variable.
// Do this so that if the same error happens again
// the alert bar will be shown again next time.
$parse(alertMessageAttr).assign(scope, null);
};
}
};
}]);
We would then add the alert bar to the HTML as follows:
<div alert-bar alertmessage="errorService.errorMessage"></div>
We need to ensure that the ErrorService is saved on the scope of the controller as
“errorService” before we add the preceding HTML. That is, if RootController was the
controller responsible for having the AlertBar, then:
Now all that needs to happen is for some controller somewhere to listen for a loginRe
quired event, and redirect to the login page (or do something more complex, like display
a modal dialog with login options).
$scope.$on('event:loginRequired', function() {
$location.path('/login');
});
That just leaves requests that will need authorization. Let us just say that all requests
that require authorization will need a header—“Authorization”—which will have a value
that is specific for the current user that is logged in. Since this will change every time,
we cannot use default transformRequests, as those are config level changes. We will
instead wrap the $http service, and create our own AuthHttp service.
We will also have an Authentication service that is responsible for storing the user’s auth
information (fetched however you want, normally as part of the login process). The
AuthHttp service will refer to this Authentication service and add the necessary headers
to authorize the requests.
return authHttp;
});
Any request that requires authorization will be made via authHttp.get() instead of
$http.get(). As long as the Authentication service is set with the right information,
your calls will fly through with ease. We use a service for Authentication as well, so that
the information is available throughout the app, without having to refetch it every time
the route changes.
That pretty much covers all the pieces we would need for this application. You should
be able to just copy the code right out of here, paste into your application, and make it
work for you. Good luck!
Conclusion
While this brings us to the end of our book, we are nowhere near close to covering
everything about AngularJS. Our aim with this book was to provide a solid foundation
from which one can begin her explorations and become comfortable with developing
in AngularJS. To this extent, we covered all the basics (and some advanced topics), while
providing as many examples as we could along the way.
Conclusion | 175
Index
We’d like to hear your suggestions for improving our indexes. Send email to index@oreilly.com.
177
debugging, 59 check boxes, 16, 161
integration tests, 56 child controller, 88
integration with RequireJS, 67 child scopes, 129
project organization, 47, 67 Chrome browser, 125
running your app, 51 Chrome desktop apps, 41
solutions to common problems, 153–174 classes, 2, 24
testing, 52, 66 click event handler, 19
tools for, 50 click notification, 20
unit tests, 54 client-side templates, 2, 57
workflow optimization, 64 Closure Compiler, 58
applications code minification, 57
analyzing, 77–100 code optimization, 58
basic startup flow, 15 combo boxes, 161
cache ability of, 15 comparisons, 27
ARIA semantic tags, 19 compilation, 57
array-style injection, 59 compile property, 120, 126
asynchronous actions, 82 computed results, 29
asynchronous requests, 112, 113 Config block, 143
attack vectors, 151 configuration files, 49
attribute datepickers, 154 controller property, 120
authentication service, 173 controller unit tests, 96
autofocus attribute, 43 (see also unit tests)
automated testing, 56 controllers
autoWatch flag, 55 adding with Yeoman, 66
basics of, 12, 13, 78
communication between, 157
B defining, 14
back buttons, 41 file location of, 48
banking attacks, 116 in Angular, 3, 7
basic optimization, 58 nesting of, 28, 133
Batarang, 60 separating UI responsibilities with, 27
bi-directional data binding, 29 vs. modules, 33
binding strategies, 129 working example of, 85
bitwise operations, 27 convenience methods, 102, 144
BlueImp’s File Upload, 162 cookies, 117, 148
bookmarks, 41 CORS, 42
boolean logic, 27 credit card objects, 108
bootstrap method, 143 cross-browser compatibility, 20, 45
bootstrap.js file, 69 CSS classes and styles, 24–26
boundaries, declaring, 12 currency filter, 37, 149
bundled filters, 37 custom error messages, 95
business logic, 78
butterbar directive, 90
D
data
C formatting with filters, 37
cacheable applications, 15 passing among scopes, 129
caching, 42, 105, 125 data binding
callbacks, 112 and templates, 14–33
camel-cased names, 121 basics of, 3
178 | Index
native speed, 31 Origin null is not allowed, 125
strategies for, 129 eval() function, 27
date filter, 37 event handlers, 18
date/time localization, 149 event handlers vs. directives, 20
datepickers, 154 event listeners, 2
debugger breakpoints, 31 event properties, 147
debugging, 59 expressions, 26, 28
declarations, 111 ExpressJS, 51
declarative event handlers, 21
deepWatch, 29
default headers, 104
F
dependency injection Factory API call, 144
basics of, 5 factory(), 35
management of, 62, 67 file upload, 162–164
organizing with modules, 33, 143 filters
dependency loops, 31 basics of, 8
deployment packages, 67 creating, 37
directive definition object, 122–125 formatting data with, 37
directive factory function, 43 using with repeaters, 157
directives Firebug, 59
API overview, 120–135 flow-of-control operators, 27
basics of, 5, 79, 84 for loop, 27
creating custom, 43, 90, 120 form elements
HTML validation and, 119 binding to model properties, 16
discounts, automatic application of, 31 hiding/showing, 23
display:block, 23 form validation controls, 45, 94
display:none, 23 forward buttons, 41, 41
do-what-I-mean declaration, 136 full propagation, 31
doA() function, 20 function binding, 155
doB() function, 20
document.cookie interface, 148 G
DOM (Document Object Model) generic services, 35
changing with directives, 43 GET requests, 116, 125
manipulation in Angular, 3, 5, 79, 132 global namespace, 14, 20
unit tests and, 21 global state, drawbacks of, 137
domReady, 69 Google Closure, 58
doSomething() function, 20 Google’s content delivery network (CDN), 11
double-curly syntax interpolation, 12, 16, 24 GutHub, 77
E H
Edit Controller, 86 Hashbang mode, 140
email app, 39 headers, setting, 104
end-to-end tests, 56 Hello, World example, 2, 4
errors History API, 142
404 errors, 26 href attribute, 26
handling of, 115 HTML Angular template files, 48
input error, 95 HTML extensions directives, 6
login errors, 171 HTML sanitization, 150
NullPointerException error, 27
Index | 179
HTML validation, 119 library, loading of, 11
HTML5, 43, 119, 138, 140 link href values, 91
HTML5 cookies, 148 link property, 120, 126
HTTP headers, setting, 104 links
HTTP protocol, 42 emailing, 41
relative links, 142
rewriting, 142
I linky filter, 152
i18n/L10n, 149 List Controller, 86, 91, 97
IDs, 2, 19, 125 lists, 21, 157
IE (Internet Explorer), 81, 122 loading, 143
if-else operator, 27 localization, 148
image tags, 26 logic
index.html, 149 avoiding in templates, 26, 79
indices, 22 business logic, 78
initialization process, 126 login errors, 171
inline event handlers, 19 looping constructs, 27
inline styles, 24
input, validation of, 45, 94
integration tests, 49, 56 M
internationalization, 148 main method, 143
isDisabled property, 24 malicious sites, 116
isolate scopes, 128 mandatory fields, 45
item property, in shopping cart example, 7 manual testing, 57
mathematics functions, 27
menus, conditional disabling of, 24
J method calls, 102
Jasmine tests, 49, 54, 96 minification, 57, 66
Java, 12 minimum/maximum field lengths, 95
JavaScript mobile apps, 41, 58
eval() function, 27 mock data, 34
main method, 143 model data
writing unobtrusive, 19 observing changes with, 29
jqLite wrapper, 132 publishing with scopes, 28, 128–131
jQuery, 4, 102, 132 storage of, 3
jQuery Datepickers, 153–157 model objects, creating, 13, 108
JS library dependencies, 48 model properties, binding elements to, 16
JS source files, 48 model variables, 12
JSON, 171 Model View Controller (MVC)
JSON vulnerability, 116 basics of, 3, 12
JSONP, 42, 171 models
JSP, 3 as basis for apps, 79
basics of, 12, 78
K model trees in Batarang, 60
Karma, 52, 55, 56, 71 module class, 144
keyboard focus, 43 modules
creation of, 14
module methods, 142–146
L number needed, 36
Law of Demeter, 5 organization of, 143
180 | Index
organizing dependencies with, 33 ng-style, 24
reasons for, 143 ng-submit, 18, 92, 94
vs. controllers, 33 ng-transclude, 126
monkey patches, 34 ng-view, 91
mousedown event handler, 19 ngPluralize, 149
multiple browser testing, 52 ngResource, 112
multiple properties, watching, 33 number filter, 37
number localization, 149
N
names/naming O
directives, 121 object properties, 29, 33, 108
parameter name matching, 36 Object.observe(), 31
services, 35 onclick, 18
namespaces ondblclick, 18
for Angular, 119 optimization, 58
providing, 14 optional fields, 45
native-speed data binding, 31 order-independent arguments, 36
nav bars, 20 Origin null…, 125
nested controllers, 28, 133
New Controller, 87
ng-app
P
declaring boundaries with, 12, 15 pagination service, 167–170
function of, 7, 143 parameter name matching, 36
working example of, 90 parameterization, 111
ng-bind, 15 parent scopes, 129
ng-class, 24, 92 parsing complexity, 34
ng-click, 18, 20 password requirement, enforcing, 95
ng-controller person objects, 108
DOM node association with, 28 PHP, 3
function of, 6, 94 plain text cookies, 148
ng-dblclick, 18 pluralization support, 149
ng-directive-name syntax, 119 POST requests, 102, 116
ng-disabled, 46, 95 price, sum total, 31
ng-eventhandler, 20 principle of least knowledge, 5
ng-hide, 23, 92 priority property, 120, 122
ng-href, 26, 91 production-ready apps, 66
ng-model project organization, 47, 67
bi-directional data binding, 29 Promise interface, 102
binding elements with, 16 Promise proposal, 113
function of, 6 promises, 82
in jQuery wrap, 155 propagation, ascertaining full, 31
in shopping cart example, 7 prototypal inheritance, 13
ng-repeat Provider API call, 145
for lists, 21, 161
function of, 7 R
priority property and, 122 radio buttons, 16
working example of, 91, 92, 94 Rails, 3, 12
ng-show, 23, 92 realtime web apps, 164
ng-src, 26
Index | 181
recipe management applications, 77 over $http, 101–106, 171–174
Regex patterns, 95 response interception, 115
relative links, 142 RESTful resources, 108–113
replace property, 120 security issues, 116–117
request transformations, 106 unit testing, 107
requests, configuring, 103 server-side configuration, 141
require property, 120, 133 servers, communicating with, 41
Requirejs, 67–74 Service API call, 145
reset buttons, 18 service dependency management, 62, 67
response interception, 115 services, 34, 35, 79, 80–83
response transformations, 106 session cookies, 117
RESTful resources, 81, 108–113 shopping cart example
RESTful servers, 81 brief explanation, 6
restrict property, 120, 122 discount application, 30
rounding up/down, 37 Simple optimization, 58
routes single-instance objects, 35
adding with Yeoman, 66 single-page applications, Angular vs. other apps,
alternatives to, 138 2
changing views with, 38–41 singleton objects, 35
list template and, 91 Smalltalk, 3
rows, highlighting, 25 Socket.IO, 164–167
Run block, 143 special headers, 104
spurious HTTP/XHR requests, 116
src attribute, 26
S static resources, 49
same origin policy, 125 styles, 24
sample application structure, 48
sanitize module, 150
Save buttons, 95 T
Scenario Runner, 56 tabbed views, 123
scenario tests, 99 tables, 21
scope property, 120 TDD (Test-Driven Development), 53
Scope.$apply, 138 Teams List app example, 157
scopes template property, 120
$scope object, 13, 24 templates
accessing from directives, 128–131 and data binding, 14–33
communicating between, 146 basics of, 78
controller inheritance, 28 canonical vs. copy of, 122
examining with Batarang, 60 client side, 2, 57
publishing model data with, 28 file location of, 48
screen flicker, 26, 113 for directives, 120, 123–125
script library, loading, 11 model setting from, 28
script tag, 90 working examples of, 89–95
search boxes, 161 writing as HTML, 5, 12
security issues, 116–117, 150–152 templateUrl property, 120, 125
selectedRow function, 26 testing
sensitive information, 116 scenario tests, 99
server authentication, 34 unit tests, 95, 107
server-side communication, 101–117 with Yeoman, 66
asynchronous requests and, 113 text inputs, 16
182 | Index
text, displaying/updating of, 15 basics of, 12, 79
then() functions, 82 changing with routes and $location, 38–41
third-party libraries, 37 creation of, 12
throw operator, 27 exposing model data to, 28
time zones, 150 working example of, 91
tokens, 117
transclude property, 120, 126
transformations, 106
W
transitive changes, 31 watchAction, 29
watchFn, 29, 31
web development platforms, 50
U web servers
UIs (User Interfaces) starting with ExpressJS, 51
creating dynamic, 4 starting with Yeoman, 51
separating responsibilities in, 27 WebSockets, 34
unauthorized transfers, 117 WebStorm development platform, 50
unit tests, 95–99 while loop, 27
for $http service, 107 window.location vs. $location, 137
for app logic, 21 Windows OS, and Yeoman, 48
for ngResource, 112 workflow optimization, 64
in Karma, 71
Jasmine style, 54
Jasmine-style, 49
X
with monkey patches, 34 XHR, 34
uppercase filter, 37 xHTML naming format, 119
user input, validation of, 45, 94 XML naming format, 119
username requirement, enforcing, 95 XSRF, 42
XSRF (Cross-Site Request Forgery) attacks, 116
V Y
validation tools, 53, 119
(see also form validation controls) Yeoman, 64
variables, in data binding, 7 overview of, 47
vendor folder, 48 starting web servers in, 51
View Controller, 86
views
adding with Yeoman, 66
Index | 183
About the Authors
Brad Green works at Google as an engineering manager. In addition to the AngularJS
project, Brad also directs Accessibility and Support Engineering. Prior to Google, Brad
worked on the early mobile web at AvantGo, founded and sold startups, and spent a few
hard years toiling as a caterer. Brad’s first job out of school was as a lackey to Steve Jobs
at NeXT Computer, writing demo software and designing Jobs’ slide presentations. Brad
lives in Mountain View, CA, with his wife and two children.
Shyam Seshadri is the owner and CEO of Fundoo Solutions, where he splits his time
between working on innovative and exciting new products for the Indian market, and
consulting about and running workshops on AngularJS. Prior to Fundoo Solutions,
Shyam completed his MBA from the prestigious Indian School of Business in Hydera‐
bad. Shyam’s first job out of college was with Google, where he worked on multiple
projects, including Google Feedback (AngularJS’s first customer!), and various internal
tools and projects. Shyam currently operates from his office in Navi Mumbai, India.
Colophon
The animal on the cover of AngularJS is a thornback cowfish (Ostraciidae). This fish of
many names—thornback, thornback cow, backspine cowfish, shortspined cowfish,
blue-spotted cowfish—is usually found on rocky reefs or sandy slopes in a tangle of
sponge and weeds in the Western Indo-Pacific region. They feed primarily on worms
and other invertebrates.
These boxfish can grow up to 15 centimeters long and anywhere between 3 to 50 meters
wide. Members of the boxfish family are recognizable by the hexagonal pattern on their
skin. Their bodies are shaped like a boxy triangle from which their fins, tail, eyes, and
mouth protrude, allowing them to swim with a rowing motion. As they age, their shapes
change from more rounded to more square-shaped, and their brighter colors dim.
The thornback cowfish protects itself by secreting cationic surfactants through their
skin, which is triggered by stress. The toxins, usually secreted in the form of a mucus,
dissolve into the environment and irritate fish in the surrounding area.
The cover image is from Johnson’s Natural History. The cover font is Adobe ITC Ga‐
ramond. The text font is Adobe Minion Pro; the heading font is Adobe Myriad Con‐
densed; and the code font is Dalton Maag’s Ubuntu Mono.