Direction on AngularJS Directives

AngularJS Directives
Angular Directives

Last week I wrote about AngularJS on a high level. This week I am going to focus on AngularJS directives, what makes them so good, what to look out for and how to create your own simple directives. All the code in today’s example is available on GitHub.

Pre-Prelude

Directives are really powerful because they allow us to define all the DOM manipulation and changes we need inside the HTML code. The temptation to mix DOM manipulation code with business logic code is lessened, which makes code easier to read, test and debug. When we write our own directives they will by default be separated from the business logic in their own directive files.

Prelude

Every good journey deserves a prelude, so for us it starts with a simple scenario. We have a service which provides us with a list of best-selling albums in JSON (I wrote a bit about JSON here). The data we get looks like this:

{
  "albums" :
  [
    {
      "name" : "Thriller",
      "artist" : "Michael Jackson",
      "released" : "1982",
      "instock" : true
    },
    {
      "name" : "The Dark Side of the Moon",
      "artist" : "Pink Floyd",
      "released" : "1973",
      "instock" : true
    },
    {
      "name" : "Bat Out of Hell",
      "artist" : "Meat Loaf",
      "released" : "1977",
      "instock" : false
    },
    {
      "name" : "Their Greatest Hits",
      "artist" : "Eagles",
      "released" : "1976",
      "instock" : true
    }
  ]
}

We are going to be creating an Angular website which will be worth millions some day (no promises on that last part). This website will list all these albums for people to admire. Also we might be selling the albums one day so the JSON will also tell us if we have the album in stock or not.

It will look something like this:

That Code Corner Music Screenshot
That Code Corner Music multi million dollar website!

This site might not win many design awards but it will be enough to help us work through how Angular directives work.

Directives in Action!

Once again you can download all the code here.

I’ve created a very simple controller and hardcoded our JSON data to a $scope.albumData variable.

angular.module('thatcodecornerMusicApp')
  .controller('MainCtrl', function ($scope) {

    $scope.albumData = {
      'albums' :
        [
          {
            'name' : 'Thriller',
            'artist' : 'Micheal Jackson',
            'released' : '1982',
            'instock' : true
          },
          {
            'name' : 'The Dark Side of the Moon',
            'artist' : 'Pink Floyd',
            'released' : '1973',
            'instock' : true
          },
          {
            'name' : 'Bat Out of Hell',
            'artist' : 'Meat Loaf',
            'released' : '1977',
            'instock' : false
          },
          {
            'name' : 'Their Greatest Hits',
            'artist' : 'Eagles',
            'released' : '1976',
            'instock' : true
          }
        ]
    };

  });
<div class="albumDetail" ng-repeat="album in albumData.albums">
    <p>Name: {{album.name}}</p>
    <p>Artist: {{album.artist}}</p>
    <p>Released: {{album.released}}</p>
    <p>In Stock: {{album.instock}}</p>
    <hr>
</div>

Then I am using a built in angular directive called ngRepeat (written ng-repeat) to iterate over the list of objects inside the albumData.albums array and I’m creating a <p> element with the album details in it for each element inside the albumData.albums array.

Normalisation

First thing people notice is that when we write the directive in HTML it’s ng-repeat however in the official documentation it’s ngRepeat. The Angular developers refer to all directives by their normalised name (ngRepeat) however because HTML is case insensitive when refering to those directives inside the DOM we need to use lower-case dash-delimited format.

There are some other normalisation rules and you can read them all here. This will become important when we start to write our own directives.

One more directive

Lets say we want to hide all the Albums where we don’t have any stock, we can use another provided directive for that. We would do that by adding a ngShow to our div and binding that to our instock variable. Now when we iterate through the albums whenever the album.instock variable is true the album will be displayed and when it’s false the album won’t be displayed.

<div class="albumDetail" ng-repeat="album in albumData.albums" ng-show="album.instock">
  ...
</div>

So what have these directives done for us.

Firstly the ngRepeat updated the DOM to display our list of our albums. If our dataset changes the DOM will be automatically updated with the changes as well.

Secondly we were able to change the way things appeared based on some value inside our dataset.

Pretty powerful, and best of all we didn’t have to write tons of Javascript code to reach our desired goal, the directives which manipulate the DOM were all placed in the HTML and not in some obscure .js file in the project, or even worse mixed in with your business code.

AngularJS comes with a plethora of directives that will usually be enough to meet most requirement but sometimes we just might want to write own.

Writing our own directives

If we look at the code at the ‘3f5c27′ commit (use git log to find the commit and git checkout 3f5c27  to get your local repository to that version) we can see that the code we used to display the albums can be extracted into it’s own directive and then we will be able to reuse that directive whenever we need to display album data.

This is how we define our directive.

angular.module('thatcodecornerMusicApp')
  .directive('tccAlbumData', function () {
    return {
      restrict: 'E',
      scope: {
        album: '=data'
      },
      templateUrl: '../views/albumdata.html'
    };
  });

There is a lot going on above, so lets go through it step by step.

.directive('tccAlbumData', ...  defines the name of our directive however two important things to note here:

  1. Whenever you create your own directives it’s important that you give it a unique prefix (tcc in this case) so that there are no collisions when Angular starts compiling your directives. Also don’t prefix your directives with ng because that’s what the Angular guys use.
  2.  When we use the directive in HTML we will do it like this  <tcc-album-data data="album"></tcc-album-data>  remember we take the normalised name tccAlbumData and change it to dash-delimited format.

restrict: 'E', forces the directive to only be used as an element and not an attribute which would be denoted as   restrict: 'A', . The directives we used above (ng-show and ng-repeat) were used as attribute directives on the <div> tag. The general rule of thumb here is to use an element when creating a component that is in control of the template and to use attributes when we want to decorate an existing element with new functionality.

The next part of the code

scope: {
    album: '=data'
},

Allows us to pass data to the directive using the  data="album" attribute in our HTML. You can read more about Isolating the Scope of a Directive here. There is a little bit more to this so I encourage you to read the official documentation.

templateUrl: '../views/albumdata.html'  lastly we tell our directive which template to use. You can view the code for the albumdata.html here.

So with these simple steps we created our own directive which we can use whenever we want to display album data in our website in a standard way.

Final Words

You would have notice that I left  ng-show="album.instock"  still in the <div> on the main.html. It could be argued that I should have moved it to the directive template that would also be fine, I left it out of the directive template because we might want to display album data in other screens even if they are out of stock.

There are other aspects to directives but this post is getting pretty long so look for a part two soon.

Until next time may you be in your element.

-R

2 Comments

  1. […] front ends and most recently front ends using AngularJS (which I’ve written about in past blogs). I enjoy writing front ends there is something rewarding in seeing your thoughts translated into […]

    2015/07/19
    Reply
  2. Martin Janes Vera said:

    Nice article 🙂 thanks!

    2015/11/27
    Reply

Leave a Reply

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