December 8, 2015

AngularJS: Understanding Custom Directive

While working on one of the applications being developed on the AngularJS framework, I came across a situation where I had to add a reusable and independent functionality on a DOM element. This could easily be done using JQuery. However, since the app was being developed using AngularJS, the best practice was to stick to that only, which was made easy with the AngularJS “Directive” tool.

Directive is one of the most powerful tools of AngularJS, and it includes attributes such as ng-show, ng-include, and ng-bind. In addition to this, AngularJS allows developers to define custom directives that express the application specifications and requirements much more clearly and semantically than regular HTML elements.

What is a Directive?

In the simplest terms, a directive in AngularJS is a reusable component. Directives in AngularJS encapsulate all the behavioral properties and functionalities of an element in a semantic way, thereby keeping all of the functionality grouped together. This helps to keep track of changes of one HTML section in one place rather than tracking the changes on a global level in a script.

A more formal definition of a directive is: In AngularJS, a directive is a JavaScript factory function defined inside an AngularJS module that holds a set of instructions for the HTML compiler for defining a specified behavior of a DOM element.

Defining a Directive

This section lists simple steps to define a custom directive in an AngularJS module. First, we need to define an Angular app.

var myApp = angular.module(‘myApp’, []);

Now, define a directive.

myApp.directive(‘myDirective’, function() {
	return {
		restrict: ‘E’,
template: ‘<h1>I made a directive!</h1>’
	};
});

This defines a directive. restrict: 'E' means "restrict the usage of this directive to only Elements." Thus we embed this directive in the HTML page as

<body ng-app=“myApp”>
	<my-directive></my-directive>
</body>

This code piece is equivalent to

<body ng-app=“myApp”>
	<h1>I made a directive!</h1>
</body>

Note that AngularJS maps the naming conventions from HTML's my-directive to JavaScript"s myDirective.

This is a basic example of definition and usage of a directive. The following is a more advanced example.

Consider a use case where we need an HTML structure to display movie titles. The HTML snippet looks like this:

<div class="movie">
    <div class="title">
        Movie Title
    </div>
    <div class="movie">
        Movie name
    </div>
</div>

The code given above seems rich in terms of CSS classes and HTML tags, but the code is not repeatable. In a case where multiple movies will need to be displayed, this code structure would become messy. Moreover, if at a later stage there comes a requirement to add a movie poster and some other functionality to the movie section, the code will need to be modified at multiple places, which is not sustainable.

Instead, we can use directive in this situation.

First, we define the Angular app and controller. Note that the list of movies is defined in the controller.

var myApp = angular.module('myApp', []);

myApp.controller('myController', function($scope) {
    $scope.movies = ['Ice Age', 'Frozen'];
});

Next, define the directive.

myApp.directive('myMovie', function() {
  return {
    restrict: 'E',
    transclude: 'true',
    template: '<span ng-transclude></span>',
    link: function(scope, element, attr){
          element.append("<strong>"+attr.title+"</strong>");
    }
  };
});

Now, embed the directive in the HTML code.

<div ng-app="myApp" ng-controller="myController">
    <div ng-repeat="movie in movies">
<my-movie title="{{movie}}">
    Movie Title :  
</my-movie>
    </div>
</div>

The output is

directive1

Ta-da!

Examine the Directive

A directive is, thus, implemented as a function, which returns an object, called Data Definition Object (DDO), that configures the directive’s behavior and template. restrict, template, etc., are the fields of this object.

restrict: defines the type of HTML element which can act as a trigger for the directive.

  • E: Element - the directive is used as an HTML tag
  • A: Attribute - the directive is used as an HTML attribute
  • C: Class - the directive is used as a CSS (in an element's class="..." definition)

transclude: specifies whether to transfer and include the original inner content of the directive's HTML markup ('Movie Title :') in the destination markup (which is defined in the template).

template: specifies the HTML content that should be added to the HTML result of the directive.

There's More...

Let's further experiment with the directive.

  1. More movies can be added to the movies in the controller.
    $scope.movies = ['Ice Age', 'Frozen', 'Aladdin', 'Tangled', 'Cars'];

    The output
    directive2

  2. Different functions for different movies can be added. Let's add some content to the movies section depending on the movie title.
    Modify the directive's link function

    link: function(scope, element, attr){
            element.append("<strong>"+attr.title+"</strong>");
            if(attr.title === 'Ice Age'){
                element.append("<br> say hi to Manny!");
            }
            else {
                element.append("<br> all hail the Snow Queen!");
            }
          }

    The output
    directive3

Angular provides several other fields in directive. When these fields are used in conjunction, they truly make directive perform magic on the HTML page.

Advantages

Directives are useful in creating repeatable and independent code. They modularize the code by clubbing requirement-specific behavioral functions in one place, rather than creating some objects in the central controller and manipulating them using multiple JavaScript methods. Such a modular code will have multiple directives that handle their own functionalities and data, and work in isolation from other directives. As an added benefit, the HTML page and Angular scripts becomes less messy and more organized.

Conclusion

In this post we learned about Custom Directives in AngularJS through a brief introduction and examples of their usages and their advantages. With Directives, you can create custom HTML tags, attributes, or classes to implement required functionality over an HTML section. This then becomes an independent and reusable component that can be embedded in the same or a different HTML page.

Should we take it further? How do directives interact with the controllers? How does it access its functions and members? We’ll explore this in the next blog, which is available here!