Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
AngularJS Web application development Cookbook
AngularJS Web application development Cookbook

AngularJS Web application development Cookbook: Over 90 hands-on recipes to architect performant applications and implement best practices in AngularJS

eBook
$28.99 $32.99
Paperback
$54.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

AngularJS Web application development Cookbook

Chapter 1. Maximizing AngularJS Directives

In this chapter, we will cover the following recipes:

  • Building a simple element directive
  • Working through the directive spectrum
  • Manipulating the DOM
  • Linking directives
  • Interfacing with a directive using isolate scope
  • Interaction between nested directives
  • Optional nested directive controllers
  • Directive scope inheritance
  • Directive templating
  • Isolate scope
  • Directive transclusion
  • Recursive directives

Introduction

In this chapter, you will learn how to shape AngularJS directives in order to perform meaningful work in your applications. Directives are perhaps the most flexible and powerful tool available to you in this framework and utilizing them effectively is integral to architecting clean and scalable applications. By the same token, it is very easy to fall prey to directive antipatterns, and in this chapter, you will learn how to use the features of directives appropriately.

Building a simple element directive

One of the most common use cases of directives is to create custom HTML elements that are able to encapsulate their own template and behavior. Directive complexity increases very quickly, so ensuring your understanding of its foundation is essential. This recipe will demonstrate some of the most basic features of directives.

How to do it…

Creating directives in AngularJS is accomplished with a directive definition object. This object, which is returned from the definition function, contains various properties that serve to shape how a directive will act in your application.

You can build a simple custom element directive easily with the following code:

(app.js)

// application module definition
angular.module('myApp', [])
.directive('myDirective', function() {
  // return the directive definition object
  return {
    // only match this directive to element tags
    restrict: 'E',
    // insert the template matching 'my-template.html'
    templateUrl: 'my-template.html'
  };
});

As you might have guessed, it's bad practice to define your directive template with the template property unless it is very small, so this example will skip right to what you will be using in production: templateUrl and $templateCache. For this recipe, you'll use a relatively simple template, which can be added to $templateCache using ng-template. An example application will appear as follows:

(index.html)

<!-- specify root element of application -->
<div ng-app="myApp">
  <!-- register 'my-template.html' with $templateCache -->
  <script type="text/ng-template" id="my-template.html">
    <div ng-repeat="num in [1,2,3,4,5]">{{ num }}</div>
  </script>  
  
  <!-- your custom element -->
  <my-directive></my-directive>
</div>

When AngularJS encounters an instance of a custom directive in the index.html template, it will compile the directive into HTML that makes sense to the browser, which will look as follows:

<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>

How it works…

The restrict: 'E' statement indicates that your directive will appear as an element. It simply instructs AngularJS to search for an element in the DOM that has the my-directive tag.

Especially in the context of directives, you should always think of AngularJS as an HTML compiler. AngularJS traverses the DOM tree of the page to look for directives (among many other things) that it needs to perform an action for. Here, AngularJS looks at the <my-directive> element, locates the relevant template in $templateCache, and inserts it into the page for the browser to handle. The provided template will be compiled in the same way, so the use of ng-repeat and other AngularJS directives is fair game, as demonstrated here.

There's more…

A directive in this fashion, though useful, isn't really what directives are for. It provides a nice jumping-off point and gives you a feel of how it can be used. However, the purpose that your custom directive is serving can be better implemented with the built-in ng-include directive, which inserts a template into the designated part of HTML. This is not to say that directives shouldn't ever be used this way, but it's always good practice to not reinvent the wheel. Directives can do much more than template insertion (which you will soon see), and it's best to leave the simple tasks to the tools that AngularJS already provides to you.

Working through the directive spectrum

Directives can be incorporated into HTML in several different ways. Depending on how this incorporation is done, the way the directive will interact with the DOM will change.

How to do it…

All directives are able to define a link function, which defines how that particular directive instance will interact with the part of the DOM it is attached to. The link functions have three parameters by default: the directive scope (which you will learn more about later), the relevant DOM element, and the element's attributes as key-value pairs.

A directive can exist in a template in four different ways: as an HTML pseudo-element, as an HTML element attribute, as a class, and as a comment.

The element directive

The element directive takes the form of an HTML tag. As with any HTML tag, it can wrap content, have attributes, and live inside other HTML elements.

The directive can be used in a template in the following fashion:

(index.html)

<div ng-app="myApp">
  <element-directive some-attr="myvalue">
    <!-- directive's HTML contents -->
  </element-directive>
</div>

This will result in the directive template replacing the wrapped contents of the <element-directive> tag with the template. This element directive can be defined as follows:

(app.js)

angular.module('myApp', [])
.directive('elementDirective', function ($log) {
  return {
    restrict: 'E',
    template: '<p>Ze template!</p>',
    link: function(scope, el, attrs) {
      $log.log(el.html());
      // <p>Ze template!</p>
      $log.log(attrs.someAttr);
      // myvalue
    }
  };
});

Note that for both the tag string and the attribute string, AngularJS will match the CamelCase for elementDirective and someAttr to their hyphenated element-directive and some-attr counterparts in the markup.

If you want to replace the directive tag entirely with the content instead, the directive will be defined as follows:

(index.html)

angular.module('myApp', [])
.directive('elementDirective', function ($log) {
  return {
    restrict: 'E',
    replace: true,
    template: '<p>Ze template!</p>',
    link: function(scope, el, attrs) {
      $log.log(el.html());
      // Ze template!
      $log.log(attrs.someAttr);
      // myvalue
    }
  };
});

This approach will operate in an identical fashion, but the directive's inner HTML will not be wrapped with <element-directive> tags in the compiled HTML. Also, note that the logged template is missing its <p></p> tags that have become the root directive element as they are the top-level tags inside the template.

The attribute directive

Attribute directives are the most commonly used form of directives, and for good reason. They have the following advantages:

  • They can be added to existing HTML as standalone attributes, which is especially convenient if the directive's purpose doesn't require you to break up an existing template into fragments
  • It is possible to add an unlimited amount of attribute directives to an HTML element, which is obviously not possible with an element directive
  • Attribute directives attached to the same HTML element are able to communicate with each other (refer to the Interaction between nested directives recipe)

This directive can be used in a template in the following fashion:

(index.html)

<div ng-app="myApp">
  <div attribute-directive="aval" 
     some-attr="myvalue">
  </div>
</div>

Tip

A nonstandard element's attributes need the data- prefix to be compliant with the HTML5 specification. That being said, pretty much every modern browser will have no problem if you leave it out.

The attribute directive can be defined as follows:

(app.js)

angular.module('myApp', [])
.directive('attributeDirective', function ($log) {
  return {
    // restrict defaults to A
    restrict: 'A', 
    template: '<p>An attribute directive</p>',
    link: function(scope, el, attrs) {
      $log.log(el.html());
      // <p>An attribute directive</p>
      $log.log(attrs.attributeDirective);
      // aval
      $log.log(attrs.someAttr);
      // myvalue
    }
  };
});

Other than its form in the HTML template, the attribute directive functions in pretty much the same way as an element directive. It assumes its attribute values from the container element's attributes, including the attribute directive and other directives (whether or not they are assigned a value).

The class directive

Class directives are not altogether that different from attribute directives. They provide the ability to have multiple directive assignments, unrestricted local attribute value access, and local directive communication.

This directive can be used in a template in the following fashion:

(index.html)

<div ng-app="myApp">
  <div class="class-directive: cval; normal-class" 
       some-attr="myvalue">
  </div>
</div>

This attribute directive can be defined as follows:

(app.js)

angular.module('myApp', [])
.directive('classDirective', function ($log) {
  return {
    restrict: 'C',
    template: '<p>A class directive</p>',
    link: function(scope, el, attrs) {
      $log.log(el.html());
      // <p>A class directive</p>
      $log.log(el.hasClass('normal-class'));
      // true
      $log.log(attrs.classDirective);
      // cval
      $log.log(attrs.someAttr);
      // myvalue
    }
  };
});

It's possible to reuse class directives and assign CSS styling to them, as AngularJS leaves them alone when compiling the directive. Additionally, a value can be directly applied to the directive class name attribute by passing it in the CSS string.

The comment directive

Comment directives are the runt of the group. You will very infrequently find their use necessary, but it's useful to know that they are available in your application.

This directive can be used in a template in the following fashion:

(index.html)

<div ng-app="myApp">
  <!-- directive: comment-directive val1 val2 val3 -->
</div>

The comment directive can be defined as follows:

(app.js)

angular.module('myApp', [])
.directive('commentDirective', function ($log) {
  return {
    restrict: 'M',
    // without replace: true, the template cannot
    // be inserted into the DOM 
    replace: true,
    template: '<p>A comment directive</p>',
    link: function(scope, el, attrs) {
      $log.log(el.html()) 
      // <p>A comment directive</p>
      $log.log(attrs.commentDirective) 
      // 'val1 val2 val3'
    }
  };
});

Formerly, the primary use of comment directives was to handle scenarios where the DOM API made it difficult to create directives with multiple siblings. Since the release of AngularJS 1.2 and the inclusion of ng-repeat-start and ng-repeat-end, comment directives are considered an inferior solution to this problem, and therefore, they have largely been relegated to obscurity. Nevertheless, they can still be employed effectively.

How it works…

AngularJS actively compiles the template, searching for matches to defined directives. It's possible to chain directive forms together within the same definition. The mydir directive with restrict: 'EACM' can appear as follows:

<mydir></mydir>

<div mydir></div>

<div class="mydir"></dir>

<!-- directive: mydir -->

There's more…

The $log.log() statements in this recipe should have given you some insight into the extraordinary use that directives can have in your application.

See also

  • The Interaction between nested directives recipe demonstrates how to allow directives attached to the same element to communicate with each other

Manipulating the DOM

In the previous recipe, you built a directive that didn't care what it was attached to, what it was in, or what was around it. Directives exist for you to program the DOM, and the equivalent of the last recipe is to instantiate a variable. In this recipe, you will actually implement some logic.

How to do it…

The far more common use case of directives is to create them as an HTML element attribute (this is the default behavior for restrict). As you can imagine, this allows us to decorate existing material in the DOM, as follows:

(app.js)

angular.module('myApp', [])
.directive('counter', function () {
  return {
    restrict: 'A',
    link: function (scope, el, attrs) {
      // read element attribute if it exists
      var incr = parseInt(attrs.incr || 1)
        , val = 0;
      // define callback for vanilla DOM click event
      el.bind('click', function () {
        el.html(val += incr);
      });
    }
  };
});

This directive can then be used on a <button> element as follows:

(index.html)

<div ng-app="myApp">
  <button counter></button>
  <button counter incr="5"></button>
</div>

How it works…

AngularJS includes a subset of jQuery (dubbed jqLite) that lets you use a core toolset to modify the DOM. Here, your directive is attached to a singular element that the directive sees in its linking function as the element parameter. You are able to define your DOM modification logic here, which includes initial element modification and the setup of events.

In this recipe, you are consuming a static attribute value incr inside the link function as well as invoking several jqLite methods on the element. The element parameter provided to you is already packaged as a jqLite object, so you are free to inspect and modify it at your will. In this example, you are manually increasing the integer value of a counter, the result of which is inserted as text inside the button.

There's more…

Here, it's important to note that you will never need to modify the DOM in your controller, whether it is a directive controller or a general application controller. Because AngularJS and JavaScript are very flexible languages, it's possible to contort them to perform DOM manipulation. However, managing the DOM transformation out of place causes an undesirable dependency between the controller and the DOM (they should be totally decoupled) as well as makes testing more difficult. Thus, a well-formed AngularJS application will never modify the DOM in controllers. Directives are tailor-made to layer and group DOM modification tasks, and you should have no trouble using them as such.

Additionally, it's worth mentioning that the attrs object is read-only, and you cannot set attributes through this channel. It's still possible to modify attributes using the element attribute, but state variables for elements can be much more elegantly implemented, which will be discussed in a later recipe.

See also

  • In this recipe, you saw the link function used for the first time in a fairly rudimentary fashion. The next recipe, Linking directives, goes into further detail.
  • The Isolate scope recipe goes over the writable DOM element attributes that can be used as state variables.

Linking directives

For a large subset of the directives you will eventually build, the bulk of the heavy lifting will be done inside the directive's link function. This function is returned from the preceding compile function, and as seen in the previous recipe, it has the ability to manipulate the DOM in and around it.

How to do it…

The following directive will display NW, NE, SW, or SE depending on where the cursor is relative to it:

angular.module('myApp', [])
.directive('vectorText', function ($document) {
  return {
    template: '<span>{{ heading }}</span>',
    link: function (scope, el, attrs) {

      // initialize the css
      el.css({
        'float': 'left',
        'padding': attrs.buffer+"px"
      });

      // initialize the scope variable
      scope.heading = '';

      // set event listener and handler
      $document.on('mousemove', function (event) {
        // mousemove event does not start $digest,
        // scope.$apply does this manually
        scope.$apply(function () {
          if (event.pageY < 300) {
            scope.heading = 'N';
          } else {
            scope.heading = 'S';
          }
          if (event.pageX < 300) {
            scope.heading += 'W';
          } else {
            scope.heading += 'E';
          }
        });
      });
    }
  };
});

This directive will appear in the template as follows:

(index.html)

<div ng-app="myApp">
  <div buffer="300" 
       vector-text>
  </div>
</div>

How it works…

This directive has a lot more to wrap your head around. You can see that it has $document injected into it, as you need to define event listeners relevant to this directive all across $document. Here, a very simple template is defined, which would preferably be in its own file, but for the sake of simplicity, it is merely incorporated as a string.

This directive first initializes the element with some basic CSS in order to have the relevant anchor point somewhere you can move the cursor around fully. This value is taken from an element attribute in the same fashion it was used in the previous recipe.

Here, our directive is listening to a $document mousemove event, with a handler inside wrapped in the scope.$apply() wrapper. If you remove this scope.$apply() wrapper and test the directive, you will notice that while the handler code does execute, the DOM does not get updated. This is because the event that the application is listening for does not occur in the AngularJS context—it is merely a browser DOM event, which AngularJS does not listen for. In order to inform AngularJS that models might have been altered, you must utilize the scope.$apply() wrapper to trigger the update of the DOM.

With all of this, your cursor movement should constantly be invoking the event handler, and you should see a real-time description of your cursor's relative cardinal locality.

There's more…

In this directive, we have used the scope parameter for the first time. You might be wondering, "Which scope am I using? I haven't declared any specific scope anywhere else in the application." Recall that a directive will inherit a scope unless otherwise specified, and this recipe is no different. If you were to inject $rootScope to the directive and log to the $rootScope.heading console inside the event handler, you would see that this directive is writing to the heading attribute of the $rootScope of the entire application!

See also

  • The Isolate scope recipe goes into further details on directive scope management

Interfacing with a directive using isolate scope

Scopes and their inheritance is something you will frequently be dealing with in AngularJS applications. This is especially true in the context of directives, as they are subject to the scopes they are inserted into and, therefore, require careful management in order to prevent unexpected functionalities. Fortunately, AngularJS directives afford several robust tools that help manage visibility of and interaction with the surrounding scopes.

If a directive is not instructed to provide a new scope for itself, it will inherit the parent scope. In the case that this is not desirable behavior, you will need to create an isolate scope for that directive, and inside that isolate scope, you can define a whitelist of parent scope elements that the directive will need.

Getting ready

For this recipe, assume your directive exists inside the following setup:

(index.html)

<div ng-app="myApp">
  <div ng-controller="MainCtrl">
    <div iso></div>
  </div>
</div>

(app.js)

angular.module('myApp', [])
.controller('MainCtrl', function ($log, $scope) {
  $scope.outerval = 'mydata';
  $scope.func = function () {
    $log.log('invoked!');
  };
})
.directive('iso', function () {
  return {};
});

How to do it…

To declare a directive with an isolate scope, simply pass an empty object literal as the scope property:

(app.js)

.directive('iso', function () {
  return {
    scope: {}
  };
});

With this, there will be no inheritance from the parent scope in MainCtrl, and the directive will be unable to use methods or variables in the parent scope.

If you want to pass a read-only value to the directive, you will use @ inside the isolate scope declaration to indicate that a named attribute of the relevant HTML element contains a value that should be incorporated into the directive's isolate scope. This can be done as follows:

(index.html)

<div ng-app="myApp">
  <div ng-controller="MainCtrl">
    <div>Outer: {{ outerval }}</div>
    <div iso myattr="{{ outerval }}"></div>
  </div>
</div>

(app.js)

.directive('iso', function () {
  return {
    template: 'Inner: {{ innerval }}',
    scope: {
      innerval: '@myattr'
    }
  };
});

With this, the scope inside the directive now contains an innerval attribute with the value of outerval in the parent scope. AngularJS evaluates the expression string, and the result is provided to the directive's scope. Setting the value of the variable does nothing to the parent scope or the attribute in the HTML; it is merely copied into the scope of the directive.

While this approach is useful, it doesn't involve data binding, which you have come to love in AngularJS, and it isn't all that more convenient than passing in a static string value. What is far more likely to be useful to you is a true whitelist of the data binding from the parent scope. This can be accomplished with the = definition, as follows:

(index.html)

<div ng-app="myApp">
  <div ng-controller="MainCtrl">
    <div>Outer: {{ outerval }}</div>
    <div iso myattr="outerval"></div>
  </div>
</div>

(app.js)

.directive('iso', function () {
  return {
    template: 'Inner: {{ innerval }}',
    scope: {
      innerval: '=myattr'
    }
  };
});

Here, you are instructing the child directive scope to examine the parent controller scope, and bind the parent outerval attribute inside the child scope, aliased as the innerval attribute. Full data binding between scopes is supported, and all unnamed attributes and methods in the parent scope are ignored.

Taking a step further, methods can also be pulled down from the parent scope for use in the directive. In the same way that a model variable can be bound to the child scope, you can alias methods that are defined in the parent scope to be invoked from the child scope but are still in the parent scope context. This is accomplished with the & definition, as follows:

(index.html)

<div ng-app="myApp">
  <div ng-controller="MainCtrl">
    <div iso myattr="func()"></div>
  </div>
</div>

(app.js)

.directive('iso', function () {
  return {
    scope: {
      innerval: '&myattr'
    },
    link: function(scope) {
      scope.innerval();
      // invoked!  
    }
  };
});

Here, you are instructing the child directive to evaluate the expression passed to the myattr attribute within the context of the parent controller. In this case, the expression will invoke the func() method, but any valid AngularJS expression will also work. You can invoke it as you would invoke any other scope method, including parameters as required.

How it works…

Isolate scope is entirely managed within the scope attribute in the directive's returned definition object. Using @, =, and &, you are instructing the directive to ignore the scopes it would normally inherit, and only utilize data, variables, and methods that you have provided interfaces for instead.

There's more…

If the directive is designed as a specific modifier for an aspect of your application, you might find that using isolate scope isn't necessary. On the other hand, if you're building a reusable, monolithic component that can be reused across multiple applications, it is unlikely that the directive will be using the parent scope in which it is used. Hence, isolate scope will be significantly more useful.

See also

  • The Recursive directives recipe utilizes the isolate scope to maintain inheritance and separation in a recursive DOM tree

Interaction between nested directives

AngularJS provides a useful structure that allows you to build channels of communication between directive siblings (within the same HTML element) or parents in the same DOM ancestry without having to rely on AngularJS events.

Getting ready

For this recipe, suppose that your application template includes the following:

(index.html)

<div ng-app="myApp">
  <div parent-directive>
    <div child-directive 
         sibling-directive>
    </div>
  </div>
</div>

How to do it…

Inter-directive communication is accomplished with the require attribute, as follows:

return {
  require: ['^parentDirective', '^siblingDirective'],
  link: function (scope, el, attrs, ctrls) {
    $log.log(ctrls);
    // logs array of in-order required controller objects
  }
};

Using the stringified directive names passed through require, AngularJS will examine the current and parent HTML elements that match the directive names. The controller objects of these directives will be returned in an array as the ctrls parameter in the original directive's link function.

These directives can expose methods as follows:

(app.js)
angular.module('myApp', [])
.directive('parentDirective', function ($log) {
  return {
    controller: function () {
      this.identify = function () {
        $log.log('Parent!');
      };
    }
  };
})
.directive('siblingDirective', function ($log) {
  return {
    controller: function () {
      this.identify = function () {
        $log.log('Sibling!');
      };
    }
  };
})
.directive('childDirective', function ($log) {
  return {
    require: ['^parentDirective', '^siblingDirective'],
    link: function (scope, el, attrs, ctrls) {
      ctrls[0].identify();
      // Parent!
      ctrls[1].identify();
      // Sibling!
    }
  };
});

How it works…

The childDirective fetches the requested controllers and passes them to the link function, which can use them as regular JavaScript objects. The order in which directives are defined is not important, but the controller objects will be returned in the order in which they are requested.

See also

  • The Optional nested directive controllers recipe demonstrates how to handle a scenario where parent or sibling controllers might not be present

Optional nested directive controllers

The AngularJS construct that allows you to build channels of communication between directive siblings or parents in the same DOM ancestry also allows you to optionally require a directive controller of a sibling or parent.

Getting ready

Suppose that your application includes the following:

(index.html)

<div ng-app="myApp">
  <div parent-directive>
    <div child-directive 
         sibling-directive>
    </div>
  </div>
</div>

(app.js)

angular.module('myApp', [])
.directive('parentDirective', function ($log) {
  return {
    controller: function () {
      this.identify = function () {
        $log.log('Parent!');
      };
    }
  };
})
.directive('siblingDirective', function ($log) {
  return {
    controller: function () {
      this.identify = function () {
        $log.log('Sibling!');
      };
    }
  };
});

How to do it…

Note that in index.html, the missingDirective is not present. A ? prefixed to the require array element denotes an optional controller directive. This is shown in the following code:

(app.js)

.directive('childDirective', function ($log) {
  return {
    require: [
      '^parentDirective',
      '^siblingDirective',
      '^?missingDirective'
    ],
    link: function (scope, el, attrs, ctrls) {
      ctrls[0].identify();
      // Parent!
      ctrls[1].identify();
      // Sibling!
      $log.log(ctrls[2]);
      // null
    }
  };
});

If the controller exists, it will be served in the same fashion as the others. If not, the returned array will be a null value at the corresponding index.

How it works…

An AngularJS controller is merely a JavaScript constructor function, and when parentDirective and siblingDirective are required, each directive returns their controller object. As you are using the controller object and not the controller scope, you must define your public controller methods on this instead of $scope. The $scope doesn't make sense in the context of a foreign directive—recall that the directive is in the process of being linked when all of this happens.

Directive scope inheritance

When a directive is not instructed to create its own isolate scope, it will inherit the scope of whatever scope it exists inside.

Getting ready

Suppose that you begin with the following skeleton application:

(index.html - uncompiled)

<div ng-app="myApp">
  <div ng-controller="MainCtrl">
    <my-directive>
      <p>HTML template</p>
      <p>Scope from {{origin}}</p>
      <p>Overwritten? {{overwrite}}</p>
    </my-directive>
  </div>
</div>

(app.js)

angular.module('myApp', [])
.controller('MainCtrl', function ($scope) {
  $scope.overwrite = false;
  $scope.origin = 'parent controller';
});

How to do it…

The most basic setup is to have the directive scope inherit from the parent scope that will be used by the directive within the link function. This allows the directive to manipulate the parent scope. This can be done as follows:

(app.js)

.directive('myDirective', function () {
  return {
    restrict: 'E',
    link: function (scope) {
      scope.overwrite = !!scope.origin;
      scope.origin = 'link function';
    }
  };
});

This will compile into the following:

(index.html – compiled)

<my-directive>
  <p>HTML template</p>
  <p>Scope from link function</p>
  <p>Overwritten? true</p>
</my-directive>

How it works…

There's nothing tricky going on here. The directive has no template, and the HTML inside it is subject to the modifications that the link function makes to the scope. As this does not use isolate scope and there is no transclusion, the parent scope is provided as the scope parameter, and the link function writes to the parent scope's models. The HTML output tells us that the template was rendered from our index.html markup, the link function was the last to modify the scope, and the link function overwrote the original values set up in the parent controller.

See also

  • The Directive templating recipe examines how a directive can apply an external scope to a transplated template
  • The Isolate scope recipe gives details on how a directive can be decoupled from its parent scope
  • The Directive transclusion recipe demonstrates how a directive handles the application of a scope to the interpolated existing nested content

Directive templating

Directives will frequently load HTML templates from outside their definition. When using them in an application, you will need to understand how to properly manage them, how they interact (if at all) with the directive's parent scope, and how they interact with the content nested inside them.

Getting ready

Suppose that you begin with the following skeleton application:

(index.html - uncompiled)

<div ng-app="myApp">
  <div ng-controller="MainCtrl">
    <my-directive>
      Stuff inside
    </my-directive>
  </div>
</div>

(app.js)

angular.module('myApp', [])
.controller('MainCtrl', function ($scope) {
  $scope.overwrite = false;
  $scope.origin = 'parent controller';
});

How to do it…

Introduce a template to the directive as follows:

(index.html – uncompiled)

<div ng-app="myApp">
  <div ng-controller="MainCtrl">
    <my-directive>
      Stuff inside
    </my-directive>
  </div>
  
  <script type="text/ng-template" id="my-directive.html">
    <div>
      <p>Directive template</p>
      <p>Scope from {{origin}}</p>
      <p>Overwritten? {{overwrite}}</p>
    </div>
  </script>
</div>

(app.js)

angular.module('myApp', [])
.controller('MainCtrl', function ($scope) {
  $scope.overwrite = false;
  $scope.origin = 'parent controller';
})
.directive('myDirective', function() {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'my-directive.html',
    link: function (scope) {
      scope.overwrite = !!scope.origin;
      scope.origin = 'link function';
    }
  };
});

This snippet will compile the directive element into the following:

(index.html – compiled)

<div>
  <p>Directive template</p>
  <p>Scope from link function</p>
  <p>Overwritten? true</p>
</div>

How it works…

The parent scope from MainCtrl is inherited by the directive and is provided as the scope parameter inside the directive's link function. The directive template is inserted to replace the <my-directive> tag and its contents, but the supplanting template HTML is still subject to the inherited scope. The link function is able to modify the parent scope as though it were the directive's own. In other words, the link scope and the controller scope are the same object in this example.

See also

  • The Directive scope inheritance recipe goes over the basics that involve carrying the parent scope through a directive
  • The Isolate scope recipe gives details on how a directive can be decoupled from its parent scope
  • The Directive transclusion recipe demonstrates how a directive handles the application of a scope to the interpolated existing nested content

Isolate scope

Often, you will find that the inheritance of a directive's parent scope is undesirable somewhere in your application. To prevent inheritance and to create a blank slate scope for the directive, isolate scope is utilized.

Getting ready

Suppose that you begin with the following skeleton application:

(index.html - uncompiled)

<div ng-app="myApp">
  <div ng-controller="MainCtrl">
    <my-directive>
      Stuff inside
    </my-directive>
  </div>
  
  <script type="text/ng-template" id="my-directive.html">
    <div>
      <p>Directive template</p>
      <p>Scope from {{origin}}</p>
      <p>Overwritten? {{overwrite}}</p>
    </div>
  </script>
</div>

(app.js)

angular.module('myApp', [])
.controller('MainCtrl', function ($scope) {
  $scope.overwrite = false;
  $scope.origin = 'parent controller';
});

How to do it…

Assign an isolate scope to the directive with an empty object literal, as follows:

(app.js)

.directive('myDirective', function() {
  return {
    templateUrl: 'my-directive.html',
    replace: true,
    scope: {},
    link: function (scope) {
      scope.overwrite = !!scope.origin;
      scope.origin = 'link function';
    }
  };
});

This will compile into the following:

(index.html – compiled)

<div>
  <p>Directive template</p>
  <p>Scope from link function</p>
  <p>Overwritten? false</p>
</div>

How it works…

The directive creates its own scope and performs the modifications on the scope instead of performing them inside the link function. The parent scope is unchanged and obscured from inside the directive's link function.

See also

  • The Directive scope inheritance recipe goes over the basics that involve carrying the parent scope through a directive
  • The Directive templating recipe examines how a directive can apply an external scope to an interpolated template
  • The Directive transclusion recipe demonstrates how a directive handles the application of a scope to the interpolated existing nested content
Left arrow icon Right arrow icon

Key benefits

  • Understand how to design and organize your AngularJS application to make it efficient, performant, and scaleable
  • Discover patterns and strategies that will give your insights into the best ways to construct production AngularJS applications
  • Get the most out of AngularJS by gaining exposure to real-world examples

Description

Packed with easy-to-follow recipes, this practical guide will show you how to unleash the full might of the AngularJS framework. Skip straight to practical solutions and quick, functional answers to your problems without hand-holding or slogging through the basics. Avoid antipatterns and pitfalls, and squeeze the maximum amount out of the most powerful parts of the framework, from creating promise-driven applications to building an extensible event bus. Throughout, take advantage of a clear problem-solving approach that offers code samples and explanations of components you should be using in your production applications.

Who is this book for?

This is not your grandmother's JavaScript cookbook. If you have a foundational understanding of the framework and want to expand your AngularJS skillset with strategies and methodologies for building performant and scaleable production applications, this is the book for you. This book assumes you have an understanding of the basics of AngularJS, and experience with JavaScript.

What you will learn

  • Architect AngularJS applications that are designed to scale
  • Implement best practices used by the top AngularJS developers
  • Write robust test suites with full application coverage
  • Create application modules with maximum reusability and extensibility
  • Master the most difficult aspects of AngularJS such as animation, testing, and promises
  • Learn how to integrate all the new components introduced in the latest 1.3 release
  • Discover syntax and browser tricks to make using AngularJS even better
  • Optimize your AngularJS application for maximum performance
Estimated delivery fee Deliver to Ukraine

Economy delivery 10 - 13 business days

$6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Dec 26, 2014
Length: 346 pages
Edition : 1st
Language : English
ISBN-13 : 9781783283354
Tools :

What do you get with Print?

Product feature icon Instant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to Ukraine

Economy delivery 10 - 13 business days

$6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date : Dec 26, 2014
Length: 346 pages
Edition : 1st
Language : English
ISBN-13 : 9781783283354
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 109.98
AngularJS Web Application Development Blueprints
$54.99
AngularJS Web application development Cookbook
$54.99
Total $ 109.98 Stars icon

Table of Contents

11 Chapters
1. Maximizing AngularJS Directives Chevron down icon Chevron up icon
2. Expanding Your Toolkit with Filters and Service Types Chevron down icon Chevron up icon
3. AngularJS Animations Chevron down icon Chevron up icon
4. Sculpting and Organizing your Application Chevron down icon Chevron up icon
5. Working with the Scope and Model Chevron down icon Chevron up icon
6. Testing in AngularJS Chevron down icon Chevron up icon
7. Screaming Fast AngularJS Chevron down icon Chevron up icon
8. Promises Chevron down icon Chevron up icon
9. What's New in AngularJS 1.3 Chevron down icon Chevron up icon
10. AngularJS Hacks Chevron down icon Chevron up icon
Index Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Full star icon Half star icon 4.5
(24 Ratings)
5 star 70.8%
4 star 16.7%
3 star 8.3%
2 star 0%
1 star 4.2%
Filter icon Filter
Top Reviews

Filter reviews by




Seif K Mar 23, 2015
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I have read many programming books but the way this book is structured is amazing. I love how the author really breaks down the framework and explains how all the piece fit in and work. I was confused with Angular since I am native iOS developer and this book really helped me understand how AngularJS works and I would recommend this book to anyone trying to dive into AngularJS.
Amazon Verified review Amazon
Stuart Hill Mar 30, 2015
Full star icon Full star icon Full star icon Full star icon Full star icon 5
When I’m looking to adopt a new technology, one of the first resources I go to is Packt. I’m not as familiar with AngularJS so when I found AngularJS Web Application Development Cookbook, it was exactly what I was looking for to get me up to speed. One of AngularJS strengths is data binding. The book has really good examples. The “How to do it” and “How it works” are very well organized and extremely helpful. More than just boilerplate, this book shows you how it works and how to get it right. This should be a go to for anyone looking for a resource on AngularJS.
Amazon Verified review Amazon
David Jones Feb 24, 2016
Full star icon Full star icon Full star icon Full star icon Full star icon 5
great for work
Amazon Verified review Amazon
NickDog Mar 20, 2015
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This book is smashing. I really enjoy reading such "recipe-like" kind of books. To learn the red-hot AngularJS, I really work up a sweat to read nearly all the books. But none really amp up my AngularJS knowledge like this one. Traditional A-Z guides always do this by disperse the implementation details among a small piece of snippet. You know what I am saying: We have to hunt through the whole chapter to put the snippet together. What a drag!! By contrast ,with over 90 hands-on recipes packed inside, the book adopt the gnarly style that all the PacktPub "cookbook" have in common.Each recipe complete a task by first telling you "How to do it" with a minimum of fluff so that you can see the result first; then it explains all the technical part to you in "How it Work" section; and additional info is in the "There's more" section. What's more, the author refers to the other related recipes in the "See also" section so that we can review all the pertinent ideas with ease. I am a big fan of this writing style: the code is given out first and then you can get to the technical details as an integral part. As an added benefit, this book can be used as a reference. If you want to perform some action, you can look it up if it's already in the book. The author just slice and dice everything for you to succeed in AngularJS world. It is a no-brainer to get a copy. Definitely recommend it to you if you want to get into the groove of AngularJS really quick and thoroughly.
Amazon Verified review Amazon
Richard Schmidt Apr 20, 2016
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Done
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the digital copy I get with my Print order? Chevron down icon Chevron up icon

When you buy any Print edition of our Books, you can redeem (for free) the eBook edition of the Print Book you’ve purchased. This gives you instant access to your book when you make an order via PDF, EPUB or our online Reader experience.

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela