AngularJS - Directives

In my previous article I described the AngularJs Filters. You can read it from the following link:

AngularJS can't keep filling in with built-in directives to quench our every desire. Today, you will see what it takes to build your own directive.

All we need is to create an app and we can start attaching directives to it. We'll see the first example:
  1. var myApp = angular.module('myApp', []);  
  2. myApp.directive('superman', function() {  
  3.     return {  
  4.     restrict: 'E',  
  5.     template: "<div> I am from Krypton. </div>"  
  6.     };  
  7. });  

This is the simplest structure of a custom directive that can be made. This directive usually returns an object with several properties; in this case, restrict and template. It can return some more properties that we may later discuss in this article. Here, restrict has the value "E" that tells the Angular to bring the directive to life if it sees any "element" with the name same as the directive's.

Other Directive Restrictions


"A" stands for "attribute", the same as the directive name.

  1. <div superman></div>  

"C" stands for "class" with the same name as the directive.

  1. <div class="superman"></div>  

"M" stands for a standard HTML comment specifying the start of the directive.

  1. <!-- directive:superman -->  
  2. <div></div>  

All these will behave in a similar way. By default, if the "restrict" property is unspecified then it is automatically set to "A"; that means we can invoke the directives by naming them as attributes in our templates.

Invoking the preceding JavaScript code in HTML:

  1. <div ng-app="myApp">  
  2. <superman></superman>  
  3. </div>  

This code is simply going to render the content in the template property of the directive's returning object. So the result will just be: "I am from Krypton." See this live example: jsfiddle

These custom attributes or directives basically add behavior to the standard HTML elements. Where do we explain this new behavior of the directive? The answer is "a linking function". Just add another property to the returning object with the name as the link. Somewhat like:

Using attributes in HTML this time:

  1. <div ng-app="myApp">  
  2.    <div superman ironman></div>  
  3. </div>  
  4. var myApp = angular.module('myApp', []);  
  5.   
  6. myApp.directive('superman', function() {  
  7. return {  
  8.     link: function() {  
  9.     alert('I can fly');  
  10.     }  
  11.   };  
  12. });  
  1. myApp.directive('ironman', function() {  
  2. return {  
  3.     link: function() {  
  4.     alert('I have an iron suit');  
  5.     }  
  6.   };  
  7. }); 

This will actually link the alert behavior to the <div> where the directives "superman" and "ironman" are used as attributes. What will the result be? Two alert messages corresponding to each directive. See it working here: jsfiddle

I might have missed one fact. If we want our directive to return an object with just one property, "link", all we can do is return a single function from the directive instead of the object. I shall use this in my next example.

The directive's behavior that we just added is of no real use. Why don't we go on with another useful implementation of directives to make the concept more clear.

I'll append a CSS class to the element when a mouse enters it and the class will be detached as soon as the mouse leaves the element. For this, I will bind certain event listeners to the element. You will see the element.bind() implementation in this example. Those who have a general idea of how jQuery works must be familiar with it.

HTML

  1. <div ng-app="myApp">  
  2.    <div enter="label" leave>This is an element</div>  
  3. </div>  

CSS

  1. .label {  
  2.    background: blue;  
  3. color: white;  
  4. }  

You will notice I have passed on a string "label" as the value of the attribute. This string specifies the name of the CSS class that is to be added and detached on mouse hover.

JavaScript

  1. var myApp = angular.module('myApp', []);   
  2.     myApp.directive('enter', function () {  
  3.     return function (scope, element, attrs) {  
  4.     element.bind("mouseenter", function() {  
  5.     element.addClass(attrs.enter);  
  6.     });  
  7.   };  
  8. });  
  9.   
  10. myApp.directive('leave', function () {  
  11.     return function (scope, element, attrs) {  
  12.       element.bind("mouseleave", function() {  
  13.        element.removeClass(attrs.enter);  
  14.        });  
  15.    };  
  16. });  

In this part of the code, one can see the parameters that were passed to the link method.

They have this order for specifying the "scope" first, the "element" reference second and the list of attributes "attrs" to this element at the third position. This "attrs" was used to read the value of the attribute "enter". See it working here: jsfiddle

Directives talking to Controllers

On your way down the path of Angular development, you may realize the need to make your directives communicate with the controllers inside that they are placed. To learn how we do it, let's use another example where we place the directive inside a controller.

  1. <div ng-app="myApp">  
  2.     <div ng-controller="appCtrl">  
  3.     <div enter>Roll over to load data</div>  
  4. </div>  
  5.   
  6. var myApp = angular.module('myApp', []);  
  7.     myApp.controller('appCtrl', function($scope) {  
  8.     $scope.loadData = function() {  
  9.     alert('loading data..');  
  10.    };  
  11. });  
  12.   
  13. myApp.directive('enter', function () {  
  14.     return function (scope, element, attrs) {  
  15.     element.bind("mouseenter", function() {  
  16.     scope.loadData();  
  17.     });  
  18.   };  
  19. });  

This example uses a function from the controller's scope inside the "enter" directive. See this working at: jsfiddle

But this might not be of much use again. The scope that we pass into the directive is the scope of the controller inside that it is placed. The method "loadData()" that we use inside the directive will work as expected only if the directive is placed inside the scope of "appCtrl". This method will go #ff0000 if the directive is placed inside any other controller.

The solution to this problem is: we can pass the name of the method as the attribute value in our template. See it in detail here: jsfiddle

There are quite a few more things to talk about Directives in AngularJS. I will be continuing this topic in the next article.

Previous article: AngularJS - Filters : Part 5
Next article: AngularJS - Directives (Contd.) : Part 6