How to create a Directive in AngularJS (simply)

When you first hear about AngularJS Directives, they appear magic in the 'omg what is going on here' way. As a newcomer to AngularJS, the idea of extending HTML with your own elements seems foreign, or a callback to IE6. Without mastering directives, you'll limit the potential of your AngularJS apps -- not cool.

It doesn't have to be this way: you can create Directives to reuse code and make templates easier for you (and future-you) to understand. You'll think of Directives as creating a DSL in HTML, and become a Wizard of unimaginable power! Ok, maybe not, but you will write easier reading code, and code that's easier to reuse.

All you need is that one example that turns the lightbulb on. Let's take an example from AngulaRails, and create a <multi-avatar> HTML element that displays a user's image, but with some requirements:

  1. If the user has a facebook-id, we want to use the Facebook image.
  2. If the user has a github username, we want to use Github's shiny avatar service.
  3. Otherwise, use Gravatar with the user's email.

So, our Interface in the HTML:

<multi-avatar data-facebook-id='' data-github-username='' data-email=''>

After AngularJS parses this Directive, the browser will see a simple:

<img src="https://example.com/555.img" alt='Avatar'/>

To create our Multi-Avatar:

  1. Create a file named 'multi-avatar-directive.js'
  2. Create a module to house our code. Since it's UI based, we use 'ui-multi-gravatar'.

    angular.module('ui-multi-gravatar', [])
    
  3. Create the directive that returns a temporary <h6> holder

    angular.module('ui-multi-gravatar', [])
     .directive('multiAvatar', [function () {
       return {
         restrict: 'E',
         link:function(scope, element, attrs) {
           var tag = '<h6>Temporary Holder</h6>';
           element.append(tag);
         }
       };
     }]);
    
  4. To get the value of a data attribute, you use attrs. So to get data-facebook-id, you get attrs.facebookId (notice the camel case). Let's use that to use Facebook, then GitHub, then Gravatar:

    angular.module('ui-multi-gravatar', [])
     .directive('multiAvatar', [function () {
       return {
         restrict: 'E',
         link:function(scope, element, attrs) {
    
           var facebookId = attrs.facebookId;
           var githubUsername = attrs.githubUsername;
           var email = attrs.email;
    
           var tag = '';
           if ((facebookId !== null) && (facebookId !== undefined) && (facebookId !== '')) {
             tag = '<h6>Use Facebook</h6>' ;
           } else if ((githubUsername !== null) && (githubUsername !== undefined) && (githubUsername !== '')){
             tag = '<h6>Use github</h6>' ;
           } else {
             tag = '<h6>Use gravatar</h6>'; 
           }
    
           element.append(tag);
         }
       };
     }]);
    
  5. Let's add some HTML to use:

    <div ng-app="YourApp">
       <div class="container" ng-controller='UsersController'>
         <div class="row">
           <div class="col-lg-4">
             <h2>With Facebook ID</h2>
             <multi-avatar data-facebook-id="717976707"/>
           </div>
           <div class="col-lg-4">
             <h2>With GitHub Username</h2>
                <multi-avatar data-github-username="jwo"/>
           </div>
           <div class="col-lg-4">
             <h2>With Email</h2>
             <multi-avatar data-email="jesse@rubyoffrails.com"/>
           </div>
           <div class="col-lg-4">
             <h2>Blank</h2>
             <multi-avatar data-facebook-id='' data-github-username='' data-email=''>
           </div>
         </div>
     </div>
    
  6. Your page will look like:
    sample-image
  7. And, let's add the image creation logic:

        if ((facebookId !== null) && (facebookId !== undefined) && (facebookId !== '')) {
          tag = '<img src="http://graph.facebook.com/' + facebookId + '/picture?type=large" class="img-responsive"/>'
        } else if ((githubUsername !== null) && (githubUsername !== undefined) && (githubUsername !== '')){
          tag = '<img src="https://identicons.github.com/' + githubUsername + '.png" class="img-responsive"/>';
        } else {
          tag = '<h6>Use gravatar</h6>'; 
        }
    

Fun! It now looks like this, with the facebook and GitHub working, leaving only gravatar to implement:

img

We need to generate an MD5 hash of the email to send to Gravatar, so we'll use the well-worn MD5 library, ported to an AngularJS service by Jim Lavin.

  1. Add <script src="../src/md5.js"></script> to your HTML
  2. Add the module to our angular app
  3. angular.module('YourApp', ['ui-multi-gravatar', 'md5']);
    
  4. Update our module to include the md5 dependency:

    angular.module('ui-multi-gravatar', [])
     .directive('multiAvatar', ['md5', function (md5) {
       return {
         restrict: 'E',
         link:function(scope, element, attrs) {
    
  5. Update our last else to use gravatars:

        } else { 
          var hash = ""
          if ((email !== null) && (email !== undefined) && (email !== '')){
            var hash = md5.createHash(email.toLowerCase());
          }
          var src = 'https://secure.gravatar.com/avatar/' + hash + '?s=200&d=mm'
          tag = '<img src=' + src + ' class="img-responsive"/>'
        }
    

And bam!

finalImage

To test out the functionality that of Gravatar for a default image:

<h2>Blank</h2>  
<multi-avatar/>

or

<h2>Blank</h2>  
<multi-avatar data-facebook-id='' data-github-username='' data-email=''>

blank

Full AngularJS Code


angular.module('ui-multi-gravatar', [])
  .directive('multiAvatar', ['md5', function (md5) {
    return {
      restrict: 'E',
      link:function(scope, element, attrs) {

        var facebookId = attrs.facebookId;
        var githubUsername = attrs.githubUsername;
        var email = attrs.email;

        var tag = '';
        if ((facebookId !== null) && (facebookId !== undefined) && (facebookId !== '')) {
          tag = '<img src="http://graph.facebook.com/' + facebookId + '/picture?width=200&height=200" class="img-responsive"/>'
        } else if ((githubUsername !== null) && (githubUsername !== undefined) && (githubUsername !== '')){
          tag = '<img src="https://identicons.github.com/' + githubUsername + '.png" style="width:200px; height:200px" class="img-responsive"/>';
        } else { 
          var hash = ""
          if ((email !== null) && (email !== undefined) && (email !== '')){
            var hash = md5.createHash(email.toLowerCase());
          }
          var src = 'https://secure.gravatar.com/avatar/' + hash + '?s=200&d=mm'
          tag = '<img src=' + src + ' class="img-responsive"/>'
        }

        element.append(tag);
      }
    };
  }]);

GitHub

This code is MIT licensed open source at github.com/jwo/angular-multi-avatar-directive. This code is heavily influenced by the super awesome Gravatar Directive by Jim Lavin.

Where to go from here

To learn more about AngularJS and how it plays nicely with Rails, sign up for our online workshop at http://angularails.com, or get on our list to hear when tickets go on sale: