English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

AngularJS Book - 1

Chapter 4. The View in Detail 

What's the view? It's what the user sees. It's the presentation layer. It's your templates. It's your markup; your HTML.  The view works closely with the controller, but it is decoupled, in that the controller doesn't know anything about the view.  Also, the view doesn't really know anything about the controller.  You may think it does, because it can execute functions and touch data within a controller; but really what the view has is a Scope object.  The view and the controller both talk to the Scope object, but not to each other.  It's here that a core paradigm of the MVC pattern becomes clear: the separation of view and business logic.  If you are familiar with the MVVM (Model-View-ViewModel) paradigm, think of the scope as the ViewModel.  The AngularJS creators have stated that AngularJS is a MVW (Model-View-Whatever) framework, which means this author is not going to tread into that discussion.

We'll discuss what an AngularJS view typically looks like, including some typical attributes and some gotchas.  This will also serve as a better introduction to directives, which are special commands executed by AngularJS.  We will also look at a filter, which you may be seeing for the first time.

Our View

Let's take a closer look at a view by improving our index.html. Right now it doesn't do squat. Change it to this:

<!DOCTYPE html>
<html ng-app="blog">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title ng-bind-template="{{title}}"></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js" type="text/javascript"
charset="utf-8"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"
charset="utf-8"></script>
<script src="blog.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div ng-controller="HeadCtrl">
<h1>{{title}}</h1>
<h3>{{description}}</h3>
</div>
<div ng-controller="ContentCtrl">
<new-post-area post="new_post" cancel-text="Cancel" save-text="Save" cancel="cancel" save="save">
</new-post-area>
<div id="{{post.id}}" ng-repeat="post in posts|orderBy:'-date'">
<ng-switch on="post.editing">
<div ng-switch-when="true">
<div class="important-background-color" color="#EEE">
<input type="text" ng-model="post.temp.title"/><br/>
<textarea ng-model="post.temp.body"></textarea><br/>
<button ng-click="cancel(post)">Cancel</button>
<button ng-click="save(post)">Save Changes</button>
</div>
</div>
<div ng-switch-default>
<h2 ng-click="edit(post)">{{post.title}}</h2>
<p ng-click="edit(post)" bold-click ng-bind-html-unsafe="post.body"></p>
<p><em>posted on {{post.date|date:'short'}} by {{post.author}}</em></p>
</div>
<hr/>
<button ng-click="newPost()">New Post</button>
</ng-switch>
</div>
</div>
<!-- directive: copyright -->
</body>
</html>

Which renders as the following:

This still isn't a whole lot, but it's a step in the right direction. We've added a title, description, more built-in directives, custom directives, and filters. Later we'll examine the controller to handle this bad boy.

Some things we've done include:

  • Dynamically generating the text in the <title> tag
  • Referencing another controller called "HeadCtrl"
  • Using a custom directive, new-post-area (or newPostArea, properly)
  • Using the ng-repeat (ngRepeat) directive to iterate over a list of Post objects
  • Use of the ng-switch (ngSwitch) directive to display some markup conditionally
  • Added the ability to edit a post
  • Added the ability to create a new post
  • Referenced another custom directive called "copyright"

As an aside, here's an example of what you can do with just three lines of JavaScript code and a view:

<html>
<script type="text/javascript" src="/path/to/angular.js"></script>
<script type="text/javascript">
angular.module('app', []).run(function($rootScope, $window) {
$rootScope.myAlert = function() {
$window.alert('Chris!');
};
});
</script>
<body ng-app="app">
<button ng-click="myAlert()">Alert My Name</button>
</body>
</html>

All that the view needs to run a function is for it to be present in the scope. If you put it in the root scope, like we have here, that is probably a dumb idea (since now basically your entire application will have this myAlert() function available to it), but for purposes of illustration, there it is. A view, a function in a scope, no models, no controllers, no services, no filters, and no custom directives.

Curlies And You

By now you may be asking yourself why some stuff like {{post.title}} is wrapped in curlies and post.body is not. The general rule is this: any reference to a model or function in your view that is not part of a directive (custom attribute or tag) will need to be wrapped in curly braces, unless otherwise specified. For example, {{post.title}} is in an HTML text node, which is not a directive. The attribute id in the <div> is also not a directive, though it is an attribute. The call to ngBindHtmlUnsafe is a directive, and we don't need curly braces there.

A counterexample is the ngHref directive, which wants curlies. You can read more about the ngHref directive in the API docs (http://docs.angularjs.org/api/ng.directive:ngHref), and why you'd use it, but to insert the value of model bar into our URL, the directive looks like:

<a ng-href="http://www.foo.{{bar}}.baz">

ngBindTemplate

In the <title> tag of our view, we have used the ngBindTemplate directive. We could have simply used <title>{{title}}</title>, right? Well, yes, but. If you did this, you would see, at least temporarily, {{title}} in your browser's title bar. We want to avoid that. Using ngBindTemplate, we can specify an entire template within the attribute. In this case, our entire template is just {{title}}. So when AngularJS is done doing its thing, it will compile your template and stuff it into the <title> tag, and then you have a proper title in the title bar without temporary ugliness.

The temporary appearance of double-curlies can be an issue with AngularJS across the board. There are ways to avoid it, including the ngCloak directive, which will hide the node it's attached to until after compilation is completed. You can attach this to the <body> tag if you wish, but if you want to see progressive rendering of your page, you are better off assigning it to nodes. Also, the clever use of ngShow and ngHide will also prevent these curlies from being briefly seen, which is my preferred method.

And our <title> tag? No ngCloak can be applied to a <head> tag; it's just a CSS trick. So our <title> tag must use ngBindTemplate if it wishes to hide its underwear.

ngRepeat

The ngRepeat directive allows you to loop over an array or object. The array syntax differs from the object syntax:

Array: <div ng-repeat="number in [1, 2, 3]">{{number}}</div>
Object: <div ng-repeat="(letter, number) in {a: 1, b: 2, c: 3}">{{letter}}: {{number}}</div>

This is all fine and dandy until you try to loop over an array and edit it in place:

<div ng-repeat="number in [1, 2, 3]">
<input type="text" ng-model="number"/>
</div>

You'd think what would happen here is you would simply be able to edit the members of the array. This is not the case. number is actually a copy of the member of the array. If you change number, AngularJS has no idea what just happened other than it knows an array has changed. Well, ngRepeat is watching that array, and will change its contents if the array changes. The array changed! ngRepeat now must rerender the template. The consequence is that once you type one character into any of the text boxes, you will lose focus because the DOM is being rerendered. Yuck!

What can we do about this? Very little, unfortunately. As it stands, if you want to loop over an array in this manner, you can, but the contents of the ngRepeat loop should be read only. A workaround is to use a different data structure. This, for example, works fine:

<div ng-repeat="number in [{n: 1}, {n: 2}, {n: 3}]">
<input type="text" ng-model="number.n"/>
</div>

You are not modifying an array at this point; you are modifying an object within the array. So the array itself has not changed, and ngRepeat does not need to rerender itself.  Good advice is to always look for a "dot" (.) in the ng-model.  

It's generally bad news if your templating language is dictating your data structures, but this is a price to pay for excellent data-binding functionality.

ngRepeat's Special Variables

The ngRepeat directive in particular has some tasty special variables. While in the context of the ngRepeat directive, you have access to the following:

  • $index -- The current, 0-based index of the ngRepeat loop. 
  • $first -- Boolean; whether we are at the first item in the loop.
  • $middle -- Boolean; whether we are not at the first or last items in the loop.
  • $last -- Boolean; whether we are at the last item in the loop.

ngBindHtmlUnsafe

That's a mouthful. What it does is allows HTML to pass through AngularJS without getting escaped. By default, AngularJS will escape any HTML before it is rendered in the view. If you want to replace the contents of a node with a bunch of actual HTML, then use this. But know that it is unsafe unless you either trust your users or have otherwise sanitized your data. For our purposes, this is OK, because we will be writing the blog posts. Comment system? Probably better to not use this directive.

What's this bold-click thing?

It's a custom directive. We'll cover that later.

Other Special Variables

We also have access to this special object in the view:

  • $event -- Object containing AngularJS event information.

 Event Bubbling

If you have an ngClick on a node, and another ngClick on the parent node, chances are any click on that child node will trigger both events.


 

For example:

<div ng-click="foo()">foo
<div ng-click="bar()">bar</div>
</div>

If you don't want foo() to get triggered, try this instead:

<div ng-click="foo()">foo
<div ng-click="bar(); $event.stopPropagation()">bar</div>
</div>


 $event is just a jQuery/jqLite Event object after all

You can pass $event to any function you are calling on your Scope, and that function can inspect the $event object for further information. Resist the temptation to use this to get at the target DOM node and start fiddling around with it. You will want to do DOM manipulation in directives or rarely services/factories. Controllers are for business logic. A reasonable use of the $event object is to inspect keystrokes, for example, and take different actions within the controller based on what key was pressed.


 

 Why Can't I Use JavaScript Function X() In My View?


AngularJS expressions are not JavaScript. They may look and smell like JavaScript, but they are not JavaScript. You cannot execute arbitrary JavaScript from an AngularJS expression, and you can't use certain operators such as ===. Things like Math.sqrt() are not available. If you want that stuff, put it in the scope: $scope.sqrt = Math.sqrt;; now you can use the sqrt() function in your view via the reference you just created.



Every Scope has a reference to a $parent scope (except the root scope or $rootScope) and a $root scope. You can use these in your view. But Scope objects prototypically inherit from their parents, so generally you will not need to use these unless you have, say, a model with the same name as a parent Scope pointing to a different object: 

Here we have controllers with models in them:

some_module.controller('MyCtrl', ['$scope', function ($scope) {
$scope.foo = 'bar';
}]);
some_module.controller('MyOtherCtrl', ['$scope', function ($scope) {
$scope.foo = 'baz';
}]);

And the corresponding view:

<div ng-controller="MyCtrl">
    <p>{{foo}} (displays "bar")</p>
<div ng-controller="MyOtherCtrl">
<p>{{foo}} (displays "baz")</p>
<p>{{$parent.foo}} (displays "bar")</p>
</div>
</div>
----

You can avoid needing to do this by simply not reusing variable names within a Scope hierarchy. Generally speaking, referring to objects using $parent is not necessary. If you are using primitives, you may have to use $parent to get what you want. In our example, anything put in the $scope object of MyCtrl will also be in the $scope object of MyOtherCtrl unless you override it.

Next, we'll discuss form controls--widgets in which users interact with your application.

Controls

Let's look at the controls.

Making a Button Click Do Something

You probably have some markup already, and it looks like this:

<button>Alert My Name</button>

To make a click on this button do something, you will use the ngClick directive. In order to do something, you must supply the ngClick directive with an AngularJS expression. Typically this will be a function, like so:

<button ng-click="myAlert()">Alert My Name</button>

Or you may even be able to get away with assignment in the markup, depending on what you wish to do:

<button ng-click="name = 'Chris'">My Name Is Now Chris</button>

But, of course, this is not a complete AngularJS application. This is, however, and will actually work:

Using Anchor (<a>) Tags

You probably will be using <a> tags somewhere in your application, and they probably don't do what the <a> tag was originally intended to do. Instead, they execute some JavaScript function. My recommendation is to write your anchor tags this way:

<a ng-click="doSomethingHeinous()">Click me</a>

Note the lack of an href attribute. You do not need # or javascript: or some such nonsense. This is on purpose; AngularJS has its own directive, and this directive will automatically prevent the default action an anchor tag is supposed to take if the href is missing.

But, if you did want to use the <a> tag in the way it was intended, you may want to do something like dynamically generate a URL. This will not work:

<a href="http://essentialvinegars.com/?id={{id}}">Buy Smelly Vinegar</a>

If the user clicks this link before AngularJS has processed the template fully, they will wind up at that URL as you have written it verbatim. You should use the ngHref directive instead:

<a ng-href="http://essentialvinegars.com/?id={{id}}">Buy Smelly Vinegar</a>

AngularJS will sit on the <a> tag and it will do nothing until the contents of ngHref are parsed.

Checkboxes


 What if I have a table, and each row has a checkbox, and we want to know which rows are "checked"?

KnockoutJS provides behavior like this, but AngularJS is different. If you want to do something like this, you will want a custom directive. If it is important that the order of the selected rows is preserved, then you will most definitely want a custom directive!


 

This example directive should help you:

myApp.directive('checkboxArray', function($parse) {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ngModel) {
var value = scope.$eval(attrs.checkboxArray),
// the thing we will put in our array specified by ngModel
array = $parse(attrs.ngModel); // getter/setter for ngModel
// if we have not defined the ngModel, let's assign an empty array to it
// otherwise we may run into problems getting the length of an undefined object
if (angular.isUndefined(array(scope))) {
array.assign(scope, []);
}
// when the checkbox is clicked, execute this
elm.bind('change', function() {
var arrayValues = array(scope),
// get the actual array out of the scope
i = arrayValues.length;
if (elm.attr('checked')) {
// if we wound up checked, push the thing onto the array specified by ngModel
arrayValues.push(value);
// need to apply since this is a jQuery event
scope.$apply(function() {
ngModel.$setViewValue(true); // checkbox will want to be true/false
array.assign(scope, arrayValues); // update the scope
});
} else {
// find the object in our array and remove it
while (i--) {
if (arrayValues[i] === value) {
arrayValues.splice(i, 1);
scope.$apply(function() {
ngModel.$setViewValue(false);
array.assign(scope, arrayValues);
});
return;
}
}
}
});
// this little gem will watch the array specified in ngModel *by value*
// and update when necessary. this way you can add/remove
// and reorder the array you specified willy-nilly, and the checkboxes will be updated.
// otherwise, if we specified ngModel.$render, it would only be called when the reference updates--
// reassigning our array to something else.
scope.$watch(attrs.ngModel, function(arrayValues, oldArrayValues) {
var i;
if (arrayValues !== oldArrayValues) {
i = arrayValues.length;
while (i--) {
if (arrayValues[i] === value) {
elm.attr('checked', true); // check the checkbox if we found the obj
return;
}
}
elm.attr('checked', false); // otherwise uncheck it
}
}, true);
}
};
});

Usage of the checkboxArray directive would look like:

<div ng-controller="MyCtrl">
<table>
<tr ng-repeat="value in values">
<td><input type="checkbox" ng-model="$parent.selectedValues" checkbox-array="value"/></td>
<td>{{value.name}}</td>
</tr>
</table>

It's important to note that since ngRepeat creates its own scope, if you simply set ngModel to be selectedValues, it would not update the model in the MyCtrl controller. Thus, use $parent. If you have already declared selectedValues in your parent scope, then nevermind.

If you want to execute a function (read: not necessarily update a model) when, say, clicking a checkbox, this can be achieved one of two ways. The first way is using the ngChange directive:

<input type="checkbox" ng-model="skub" ng-change="congratulateProSkub()">I am pro-skub</input>

And in your controller you would have: 

$scope.congratulateProSkub = function() {
if ($scope.skub) {
$window.alert('Thanks for being pro-skub');
}
};

That's all well and good, but note that two things actually happen when you check (or uncheck) that box. Your function will get executed, of course, but also a boolean model (skub) will be updated. The value of $scope.skub in the controller is now true if this checkbox is checked. If we wished to keep this bit of logic out of the view, we could do something like this:

<input type="checkbox" ng-model="skub">I am pro-skub</input>

And:

$scope.$watch('skub', function(new_val, old_val) {
if (new_val !== old_val) {
$scope.congratulateProSkub();
}
});

This accomplishes the same thing. Heck, you could even combine the two and three things would happen when you clicked the checkbox. It's probably personal preference as to which one you should be using; I see pros and cons for either, and none of those pros or cons are especially compelling.

Radios

Radios can be slightly different than the way you'd normally use a radio:

<input type="radio" name="foo" value="bar"/> Bar<br/>
<input type="radio" name="foo" value="baz"/> Baz

In AngularJS, you don't necessarily need the name attribute unless you want to do form validation (more on this later). Instead, you may do this:

<input type="radio" ng-model="foo" value="bar"/> Bar<br/>
<input type="radio" ng-model="foo" value="baz"/> Baz

And it will work exactly like in the first example, with the added bonus of assigning the value to foo in the scope.

Selects

Selects may also be a little different. Maybe you have an array of strings (and it's OK if you do):

<select ng-model="foo">
<option ng-repeat="thing in ['bar', 'baz']">{{thing}}</option>
</select>

The value of foo will the "bar" or "baz", probably just like you wanted. But what if we were iterating over an array of objects?

<select ng-model="foo">
<option ng-repeat="thing in [{name: 'bar'}, {name: 'baz'}]">{{thing.name}}</option>
</select>

What ends up in $scope.foo? Well, the strings "bar" or "baz" again. This may not be what you wanted however, as there's nothing that tells you precisely which object was selected! If you wanted to find that out, you'd have to create another lookup object, or iterate over the whole array looking for the object with the same name as $scope.foo. No. There is a better way:

<select ng-model="foo" ng-options="thing.name for thing in [{name: 'bar'}, {name: 'baz'}]"></select>

Now the value of $scope.foo is actually the object in the array itself. This is incredibly useful. It's like you made an entire object, not just a string, be the value attribute of your option tag. Very cool indeed.

Basically you will be iterating over one of two kinds of data structures: an array or an object. If you have an array, the value of ngOptions will be "label for value in array"; where "label" is what is displayed to the user, "value" is what is stored in the ngModel, and of course the array itself.

If you have an object, you don't care about order. Your markup may look like the following:

<select ng-model="foo" ng-options="key + ': ' + value.name for (key, value) in {bar: {name: 'barth'}, baz: {name: 'bazalthar'}}"></select>

This displays "bar: barth" and "baz: bazalthar" in the select dropdown; when you choose something the value of $scope.foo becomes the object referred to by the key ("bar" and "baz"). In general terms, that's "label for (key, value) in object".

Everybody loves lists and tables and that's what we're going to talk about next.  

Lists and Tables

If you haven't figured this one out by now, your go-to directive for iterating over arrays or objects is the ngRepeat directive. This directive is a bit curious as it does two things, which will look like bugs if you don't know what's going on: it creates a scope for each iteration and it iterates over a copy of the data, not the data itself. Thus, you will run into problems if you are iterating over an array and giving the user power to edit anything in that array. As soon as you edit something, a watch fires and redraws. This does not work well:

<ul>
<li ng-repeat="item in ['foo', 'bar', 'baz']">
<input type="text" ng-model="item"/>
</li>
</ul>

In fact, as of version 1.0.5, AngularJS enforces an array of this type remains read-only. A workaround would be to change the array to look something like [{name: 'foo'}, {name: 'bar'}, {name: 'baz'}] and set your ngModels to be item.name, then you may edit your objects with impunity. 

ngRepeat has a handful of special variables it injects into its iteration scopes:

  • $index: The numeric value of the iteration; if array, current array index
  • $first: Boolean; whether or not we are at the first iteration
  • $last: Boolean, whether or not we are at the last iteration
  • $middle: Boolean, whether or not we are neither at the first nor last iteration

 A quick way to determine if an array only has one value

Use the expression $first && $last. This will only be true if the first value in the array is the last value in the array; in other words, an array of length one (1).



There are many use cases for something like this, suffice it to say. Use these variables and you can render your HTML differently depending on where you are in the loop.

Sorting and Filtering

AngularJS has built-in sorting and filtering filters that are ideal for use with ngRepeat. We use ngRepeat to display our blog posts. Let's take a closer look. What follows is an HTML fragment to display the posts:

<div id="{{post.id}}" ng-repeat="post in posts|orderBy:'-date'">
<!-- bunch of stuff eliminated for sake of brevity -->
</div>

The way it's set up now, we'll show the newest post first down through the oldest post. Let's make this dynamic:

<div id="{{post.id}}" ng-repeat="post in posts|orderBy:order:descending">
<!-- bunch of stuff eliminated for sake of brevity -->
</div>

And back in our controller:

blog.controller('ContentCtrl', function ($scope, Posts, Post) {

.. snip ..

$scope.order = 'date';
$scope.descending = true;
});

Now, we can modify order and descending to sort how we wish. Let's make a couple form controls to change this:

<div id="posts-form">
Order By: <select ng-model="order">
<option value="title">Title</option><option value="date">Date</option></select>
<input type="checkbox" ng-model="descending"/> Descending
</div>
<div id="{{post.id}}" ng-repeat="post in posts|orderBy:order:descending">
<!-- bunch of stuff eliminated for sake of brevity -->
</div>

It looks something like this:

Swell! Let's take it a step further and add a filter box. We'll use the filter filter for this:

<div id="posts-form">
Order By: <select ng-model="order"><option value="title">Title</option><option value="date">
Date</option></select>
<input type="checkbox" ng-model="descending"/> Descending <br/>
Filter: <input type="text" ng-model="filter"/>
</div>
<div id="{{post.id}}" ng-repeat="post in posts|orderBy:order:descending|filter:filter">
<!-- bunch of stuff eliminated for sake of brevity -->
</div>

Yes, you may combine filters.  That's all we need to do a full text filter across all of the fields of a Post object. As you type, the filter will be updated and your results will show immediately. To accomplish this you'll note we didn't have to modify the controller at all. Filters, combined with ngModel, are a lethal combination indeed.

By default, this will filter on all values of an object. If you only want to filter on one value, say, the title of an object, you will need a custom filtering function. For example:

$scope.myFilteringFunction = function(item) {
return item.title.match($scope.filter);
};

Now you can do this:

<div id="{{post.id}}" ng-repeat="post in posts|orderBy:order:descending|filter:myFilteringFunction">
<!-- bunch of stuff eliminated for sake of brevity -->
</div>

Even/Odd Rows

It's often highly desirable to change the CSS of a row in a table based on whether it's an even or odd row. This is incredibly easy to do in AngularJS using the ngClassEven and ngClassOdd directives:

<div id="{{post.id}}" ng-repeat="post in posts|orderBy:order:descending|filter:filter" 
ng-class-even="'even'" ng-class-odd="'odd'">
<!-- bunch of stuff eliminated for sake of brevity -->
</div>

Now, each <div> in our list of posts will be tagged with either an even class or an odd class, depending. Simply set up your CSS and have one of these alter the background color slightly, and you're set. Feel free to use a table instead.

Temporary Variables

You are probably not going to need temporary variables. You may think you do -- what if we have a nested ngRepeat loop, and we need to access the $index property of the outer loop from within the inner loop? The inner loop declares $index itself, so that's not going to work. In other templating languages you might want to declare a temporary variable within the outer loop. This is how you'd do it with AngularJS:

<div ng-repeat="stuff in things" ng-init="outerIndex = $index">
<div ng-repeat="bits in stuff">
{{outerIndex}}.{{$index}}
</div>
</div>

But, in AngularJS, you don't need to use ngInit for this common case. ngInit simply executes an AngularJS expression against the current scope. Use it sparingly! Remember that every scope has a parent (except the root scope, of course). To avoid ngInit, do this:

<div ng-repeat="stuff in things">
<div ng-repeat="bits in stuff">
{{$parent.$index}}.{{$index}}
</div>
</div>

In fact, I'm having a hard time imagining a use case wherein $parent wouldn't be sufficient in some way. However, I'd suggest using ngInit if you end up with something like $parent.$parent.$parent.$parent.$index. Do whatever is simple.  Also remember that ngInit, unless it's in an ngRepeat loop, will only execute one time; use this to your advantage or peril, if you must.

What if you have a bunch of JavaScript that you would like injected into the DOM, but it doesn't make sense to escape it in JavaScript code?  Templates to the rescue.  We'll discuss them next.

Templates

Generally you'll just be wildly tossing AngularJS directives into your raw markup, so you won't need a template per se. However, when it comes to lazy loading HTML or otherwise, consider using a template. It's also great to use small templates for reusability.  There are two ways to define templates. The first is in a <script> tag: 

<script type="text/ng-template" id="foo">
Hi, I'm the template with ID "foo".
</script>

The advantage of having this stuff in a <script> tag like this is that your templates stay out of the DOM until you put them there. The second form of a template, which shares this advantage, is a partial. A partial is simply an external HTML file. These are both templates and can be used exactly the same way, the only difference is that one is external and the other is not.

Including a Template

The most straightforward way to include a template is the ngInclude directive. Simply give it a path or ID, and it will first attempt to retrieve it from the template cache (see below), and if this misses, an HTTP request will be made to the path. If your application is small and you're not concerned about loading times, you should use internal (<script>-style) templates, because they will be gathered up by AngularJS.  If you prefer lazy-loading or when loading a bunch of HTML up front is a bad idea, use the external (partial) style. Let's break out our index.html into some partials:

<!DOCTYPE html>
<html ng-app="blog">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title ng-bind-template="{{title}}"></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js" type="text/javascript"
charset="utf-8"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"
charset="utf-8"></script>
<script src="blog.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<ng-include src="'header.html'"></ng-include>
<div ng-controller="ContentCtrl">
<new-post-area post="new_post" cancel-text="Cancel" save-text="Save" cancel="cancel"
save="save"></new-post-area>
<div id="posts-form">
Order By: <select ng-model="order"><option value="title">Title</option><option value="date">
Date</option></select>

<input type="checkbox" ng-model="descending"/> Descending <br/>
Search: <input type="text" ng-model="filter"/>
</div>
<div id="{{post.id}}" ng-repeat="post in posts|orderBy:order:descending|filter:filter" ng-class-even="
'even'" ng-class-odd="'odd'">

<ng-switch on="post.editing">
<div ng-switch-when="true">
<div class="important-background-color" color="#EEE">
<input type="text" ng-model="post.temp.title"/><br/>
<textarea ng-model="post.temp.body"></textarea><br/>
<button ng-click="cancel(post)">Cancel</button>
<button ng-click="save(post)">Save Changes</button>
</div>
</div>
<div ng-switch-default>
<h2 ng-click="edit(post)">{{post.title}}</h2>
<p ng-click="edit(post)" bold-click ng-bind-html-unsafe="post.body"></p>
<p><em>posted on {{post.date|date:'short'}} by {{post.author}}</em></p>
</div>
<hr/>
<button ng-click="newPost()">New Post</button>
</ng-switch>
</div>
</div>
<ng-include src="'footer.html'"></ng-include>
</body>
</html>

Here is the header.html:

<div ng-controller="HeadCtrl">
<h1>{{title}}</h1>
<h3>{{description}}</h3>
</div>

And here is the footer.html:

<!-- directive: copyright -->
----

That's it! As you can see, ngInclude expects an AngularJS expression for the src attribute; that way you can dynamically change which template ID you are after.

If you want to include a template from within a directive (outside of the context of the template or templateUrl arguments), you can mimic what ngInclude does in a manner akin to this:

myApp.directive('myInclude', function ($templateCache, $http, $q, $compile) {
return {
restrict: 'E',
link: function (scope, elm, attrs) {
// retrieves a template from the template cache over http as necessary.
// returns a promise.
(function () {
var dfrd, template = $templateCache.get(scope.$eval(attrs.src));
if (template) {
dfrd = $q.defer();
dfrd.resolve(template);
return dfrd.promise;
} else {
return $http.get(scope.$eval(attrs.src), {}, {cache: $templateCache });
}
})().then(function (res) {
elm.replaceWith($compile(res.data)(scope));
});
}
};
});

And you can use it this way, just as you would ngInclude:

<my-include src="'foo.html'"></my-include>

Of course, since ngInclude already exists, this is a futile exercise, but it shows you how you can retrieve templates on-the-fly from within a directive if you need to.

There is yet another way to include a template, and that is the ngView directive. To use it you must be using the $route service.

The Template Cache

Note that templates are cached within the $templateCache service. If you have defined a template in a <script> tag, it will be loaded by AngularJS and cached for later. When you go to retrieve it, it has already been cached and simply needs to be compiled against the scope at that point. Partials, however, are not immediately loaded, so they will not be in the $templateCache until after the first time you load them. When a template is put in the template cache, it's indexed by its ID. The ID of our template above is foo, obviously enough. However, partials don't have this attribute, but they do have IDs. The ID of a partial is simply the full path it was referenced with. For example, if you have a partial living in /static/partials/foo.html, /static/partials/foo.html is the ID.

Templates are cached by default as it's a rare occurrence in which this behavior is undesirable. If you want to remove template with ID id, use $templateCache.remove(id).


 Hey, AngularJS keeps escaping my HTML!


By default, AngularJS escapes all HTML rendered to a template. Generally, this saves you from yourself if your data needs to be scrubbed. If you trust your source, you can have AngularJS render HTML verbatim using the ngBindHtmlUnsafe directive:

<div ng-bind-unsafe="some_html"></div>

Under the Hood

Compilation in AngularJS is a two-step process. Consider the $compile service, and a raw, unprocessed AngularJS template named template:

var linker = $compile(template);

The above represents the first phase of the compilation process; the "compile" step (confusing, I know). What happens here is the template is examined for directives. AngularJS collects them together and produces a linking function (directives may specify a compile function of their own to be specified during this step). At this point, no data has been inserted into the template. Then, we execute the linker function against a scope, which is the "link" phase:

var compiled = linker(scope);

This phase passes a scope and its data into the link function of each directive. What we get back from linker is an AngularJS element representing our DOM fragment. We can then happily insert this element into the page's DOM, or whatever we wish to do with it. Frequently when using the $compile service, you'll be doing both phases at once: $compile(template)(scope).

The same thing happens when AngularJS encounters your markup for the first time. It scans all of the markup for directives, creates a link function made of all of the directives' link functions, then executes that function using the appropriate scope.

The reason for these two phases is simply one of performance. By compiling once and linking as the data-binding mechanism executes, we save rerendering a gigantic chunk of markup (your raw, unprocessed template) that won't change.


 Unit Testing The View


You will likely want to be writing functional tests for your views, not unit tests. A tool like Selenium (http://seleniumhq.org) is well-suited for this task, but as I mentioned earlier, we're not going to cover functional tests as they are beyond the scope of this book.


Conclusion

The view is simply the presentation logic for AngularJS.  It integrates models, directives, filters and serves as the primary point of interface into your application.  If someone needs to "save", they do so by clicking a button; not typing a command into their JS console!  Though I suppose you could write a text adven...oh, nevermind.

Next we'll cover the controller, where business logic lives.

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.