Using Binding and Template Directives in AngularJS

1. Why and When to Use Directives

Directives are the signature feature of AngularJS, setting the overall style of AngularJS development and the shape of an AngularJS application. Other JavaScript libraries—including the much-loved jQuery—treat the elements in an HTML document as a problem to be overcome, requiring manipulation and correction before they can be used to create a web application.

The AngularJS approach is different: You create AngularJS web apps by embracing and enhancing HTML and treating it not as a problem but a foundation on which to build application features. It can take a little while to get used to the way that directives work—especially when you start to create your own custom HTML elements, a process I describe in Chapter 16—but it becomes second nature, and the result is a pleasing mix of standard HTML mixed with custom elements and attributes.

AngularJS comes with more than 50 built-in directives that provide access to core features that are useful in almost every web application including data binding, form validation, template generation, event handling, and manipulating HTML elements. And, as I already mentioned, you use custom directives to apply your application’s capabilities.

Table 10-2 summarizes why and when to use directives in an AngularJS application.

2. Preparing the Example Project

To prepare for this chapter, I deleted the contents of the angularjs web server folder and installed the angular.js, bootstrap.css, and bootstrap-theme.css files, as described in Chapter 1. I then created a file called directives.html, which you can see in Listing 10-1.

Listing 10-1. The Contents of the directives.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Directives</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script>

angular.module(“exampleApp”, [])

.controller(“defaultCtrl”, function ($scope) { $scope.todos = [

{ action: “Get groceries”, complete: false },

{ action: “Call plumber”, complete: false },

{ action: “Buy running shoes”, complete: true }, { action: “Buy flowers”, complete: false },

{ action: “Call family”, complete: false }];

});

</script>

</head>

<body>

<div id=”todoPanel” class=”panel” ng-controller=”defaultCtrl”>

<h3 class=”panel-header”>To Do List</h3>

Data items will go here…

</div>

</body>

</html>

This is a skeletal outline for the classic to-do list application (one of the reasons that so many web app examples are based on to-do lists is because lists of data objects are perfect for demonstrating template techniques).

You will recognize some of the AngularJS components that I described in Chapter 9. I created a module called exampleApp using the angular.module method and then used the fluent API to define a controller called defaultCtrl. The controller uses the $scope service to add some data items to the data model, and the module and the controller are applied to HTML elements with the ng-app and ng-controller directives. You can see how the initial content in the directives.html file is displayed by the browser in Figure 10-1.

Tip You can treat the content of Listing 10-1 as a black box for the moment, get a brief description about each component from Chapter 9, or consult the chapters later in the book where I describe these building blocks in detail.

3. Using the Data Binding Directives

The first category of built-in directives is responsible for performing data binding, which is one of the features that elevates AngularJS from a template package into a full-fledged application development framework. Data binding uses values from the model and inserts them into the HTML document. Table 10-3 describes the directives in this category, and I demonstrate their use in the sections that follow.

Data binding is incredibly important in AngularJS development, and you will rarely encounter any substantial fragment of HTML in an AngularJS application that doesn’t have some kind of data binding applied to it, and as you’ll learn in the next section, the functionality provided by the ng-bind directive is so central to AngularJS that it has an alternative notation so you can create data bindings more easily

3.1. Performing One-Way Bindings (and Preventing Them)

AngularJS supports two kinds of data binding. The first, one-way binding, means a value is taken from the data model and inserted into an HTML element. AngularJS bindings are live, which means that when the value associated with the binding is changed in the data model, the HTML element will be updated to display the new value.

The ng-bind directive is responsible for creating one-way data bindings, but it is rarely used directly because AngularJS will also create this kind of binding whenever it encounters the {{ and }} characters in the HTML document. Listing 10-2 shows the different ways you can create one-way data bindings.

Listing 10-2. Creating One-Way Data Bindings in the directives.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Directives</title>

<script src=”angular.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script>

angular.module(“exampleApp”, [])

.controller(“defaultCtrl”, function ($scope) {

$scope.todos = [

{ action: “Get groceries”, complete: false },

{ action: “Call plumber”, complete: false },

{ action: “Buy running shoes”, complete: true },

{ action: “Buy flowers”, complete: false },

{ action: “Call family”, complete: false }];

});

</script>

</head>

<body>

<div id=”todoPanel” class=”panel” ng-controller=”defaultCtrl”>

<h3 class=”panel-header”>To Do List</h3>

<div>There are {{todos.length}} items</div>

<div>

There are <span ng-bind=”todos.length”></span> items </div>

<div ng-bind-template=

“First: {{todos[0].action}}. Second: {{todos[l].action}}”>

</div>

<div ng-non-bindable>

AngularJS uses {{ and }} characters for templates

</div>

</div>

</body>

</html>

You can see the result of navigating to the directives.html file with the browser in Figure 10-2. The effect isn’t the most visually striking, but the directives in the example are doing some interesting things.

Tip AngularJS isn’t the only JavaScript package that uses the {{ and }} characters, which can be a problem if you are trying to make multiple libraries work together. AngularJS allows you to change the characters used for inline bindings; I explain the process in Chapter 19.

The first two data bindings in this example are equivalent. I have used {{ and }} to denote a one-way binding for the number of items in the $scope.todos collection:

<div>There are {{todos.length}} items</div>

This is the most natural and expressive way of creating data bindings: The bindings are easy to read and fit naturally into the content of HTML elements. The second data binding uses the ng-bind directive, which has the same effect but requires an additional element:

There are <span ng-bind=”todos.length”></span> items

The ng-bind directive replaces the content of the element that it is applied to, which means I have to add a span element to create the effect I want. I don’t use the ng-bind directive in my own projects; I prefer the inline bindings.

The ng-bind directive does allow you to hide your template markup when the HTML content is shown to the user before it is processed by AngularJS (because browsers don’t display attribute values to users), but this is rarely a problem and is addressed by the ng-cloak directive, which I describe later in this chapter.

Aside from being a little awkward to use, the ng-bind directive is limited to being able to process a single data binding expression. If you need to create multiple data bindings, then you should use the ng-bind-template directive, which is more flexible, as follows:

<div ng-bind-template=”First: {{todos[0].action}}. Second: {{todos[1].action}}“></div>

The value I specified for the directive contains two data bindings, which the ng-bind directive would not be able to process. I have never used this directive in a real project, and I suspect I never will. I include this directive here for completeness only.

3.2. Preventing Inline Data Binding

The drawback of the inline bindings is that AngularJS will find and process every set of {{ and }} characters in your content. This can be a problem, especially if you are mixing and matching JavaScript toolkits and want to use some other template system on a region of HTML (or if you just want to use double-brace characters in your text). The solution is to use the ng-non-bindable directive, which prevents AngularJS from processing inline bindings:

<div ng-non-bindable>

AngularJS uses {{ and }} characters for templates

</div>

If I had not applied the directive, AngularJS would have processed the contents of the div element and then tried to bind to a model property called and. AngularJS doesn’t complain when it is asked to bind to a nonexisting model property because it assumes it will be created later (as I explain when I describe the ng-model directive later in this chapter). Instead, it inserts no content at all, which means that instead of the output I wanted:

AngularJS uses {{ and }} characters for templates

I would instead produce this:

AngularJS uses characters for templates

4. Creating Two-Way Data Bindings

Two-way data bindings track changes in both directions, allowing elements that gather data from the user to modify the state of the application. Two-way bindings are created with the ng-model directive, and as Listing 10-3 demonstrates, a single data model property can be used for both one- and two-way bindings.

Listing 10-3. Creating Two-Way Bindings in the directives.html File

<body>

<div id=”todoPanel” class=”panel” ng-controller=”defaultCtrl”>

<h3 class=”panel-header”>To Do List</h3>

<div class=”well”>

<div>The first item is: {{todos[0].action}}</div>

</div>

<div class=”form-group well”>

<label for=”firstItem”>Set First Item:</label>

<input name=”firstltem” class=”form-control” ng-model=”todos[0].action” />

</div>

</div>

</body>

There are two data bindings in this listing, both of which are applied to the action property of the first object in the todos data array (which I set up using the $scope object in the controller and reference in bindings as todos[0].action). The first binding is an inline one-way binding that simply displays the value of the data property, just as I did in the previous example. The second binding is applied via the input element and is a two-way binding:

<input name=”firstItem” class=”form-control” ng-model=”todos[0].action” />

Two-way bindings can be applied only to elements that allow the user to provide a data value, which means the input, textarea, and select elements. The ng-model directive sets the content of the element it is applied to and then responds to changes that the user makes by updating the data model.

Tip The ng-model directive provides additional features for working with HTML forms and even for creating custom form directives. See Chapters 12 and 17 for details.

Changes to data model properties are disseminated to all of the relevant bindings, ensuring that the application is kept in sync. For my example, this means that changes to the input element update the data model, which then causes the update to be shown in the inline one-way binding.

To see the effect, use the browser to navigate to the directives.html document and edit the text in the input element; you will see that the one-way binding is kept in sync with the contents of the input element, all through the magic of the two-way binding. The best way to experience this effect is by re-creating the example and experiencing it first hand, but you can get a sense of what happens in Figure 10-3.

Note OK, two-way bindings are not really magic. AngularJS uses standard JavaScript events to receive notifications from the input element when its content changes and propagates these changes via the $scope service. You can see the event handler that AngularJS sets up through the F12 developer tools, and I explain how the $scope service detects and disseminates changes in Chapter 13.

Tip In this example, I used properties that had been explicitly added to the data model through the $scope service in the controller factory method. One nice feature of data binding is that AngularJS will dynamically create model properties as they are needed, which means you don’t have to laboriously define all of the properties you use to glue views together. You can see further examples of this technique in Chapter 12 when I describe the AngularJS support for working with form elements.

Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.

Leave a Reply

Your email address will not be published. Required fields are marked *