AngularJS has many directives to manipulate the DOM, delegate events to event handler functions, perform data binding and much more. I am pretty sure you already used directives in Angular. There are really many of them in angular, such as ng-click, ng-show/ng-hide or ng-repeat. You can find more here: https://docs.angularjs.org/api/ng/directive.

Even if there are many directives ready to use by Angular, you may face the day where you want to create your own directive. There are various reasons to create your own directive such as reusing of functionality across your application. In this post I want to give a quick introduction in how to create your own directive.

Invoking a directive

There are currently four ways of invoking a directive, as an attribute, as an element, as a class or as a comment. There is to say, that attributes and elements are considered as the way to use since it is the much cleaner way & if you want to support older browsers, then you should consider to use attributes.

 

As an attribute:

 

As an element:

 

As an class:

 

As an comment:

 

When creating your own directive, you can specify which of these ways you want to support with the restrict property. Attribute (A), Element (E), Class (C) and Comment (M).

Our first directive

We can define our first directive as follows:

And invoke it in our html as follows:

 

As an attribute:

 

As an element:

Notice, that we are declaring it as myDirective, but invoking it as my-directive, this is because Angular transforms our camel case to snake case. What do we have here? So we are returning a directive description object with currently two properties.

  • restrict: We are restricting the use of this directive to either an attribute or element.
  • template: The template which we want to include.

We could also put the above template into a file and tell the directive the location of the file like:

  • template-Url: ‘path/to/file.html’.

More properties

require option

If our directive is driven by an input, we could say that it is required that for example the ng-model directive is invoked with our directive or as a parent. With the require option, we are binding another controller to our directive.

Now we could invoke our directive like this:

Notice the ^ in front of ngModel. When a directive uses this option, $compile will throw an error unless the specified controller is found. The ^ prefix means that this directive searches for the controller including its parents (without the ^ prefix, the directive would look for the controller on just its own element).

If you require more than one controller, you can simply declare it as an array:

If doing this with your own directive, make sure you have the controller option included as well, because it is needed.

controller option

To create a controller in our directive we have to use the controller option. This controller is then instantiated before the pre-link phase. The pre- and post-linking phases are executed by the $compiler. The pre- and post-link functions are executed before and after the child elements are linked. I will explain that later in a little bit more detail.

It is important to note that we cannot do DOM manipulations in our controller function.

Luckily, the controller function look like any other controller. We can inject the $resource service in our controller for example.

As you see, we are calling from our link function our controller functions, which sets the myIp scope variable after the promise is successful. A promise is basically an object that will return the result or an error of an action asynchronously.

Now we can use our directive:

See here a working plunker: http://plnkr.co/edit/llO9mV

scope option

Like a controller has its own scope, we can give also to a directive their own scope. There are three ways of declaring a scope in a directive. Using parents controller, inherit from parent scope or create an isolated scope. This is very important to know, since if no using an isolated scope in our directive, can cause some unexpected behaviour. To declare one of the above mentioned scopes you will need the scope option or not :).

 

Use parents scope

We can leave the scope option at all to use the parents scope. This is the same as you declare the scope as false like:

 

Create inherited child scope

We can also inherit from the parents scope, by declaring the scope to true.

This will prototypically inherit all properties from the parent. This means, we can use the properties from the parent, but if we create a new one, it is not populated to the parent. But the danger is still here to have some unexpected behaviour and its anyway bad to use directly properties from the parents scope.

 

Create isolated child scope

We can create an isolated scope by passing an object to the scope option.

This scope will not mess with the existing parent scope which is the preferred way of using it. But this means now, that we have an completely empty scope and we will have to pass things from the outside world to our inner world.

For that, we can bind data with the following three aliases in the object:

@ scope property

The @ scope property is used to access simple values that are defined outside the directive. Everything will be evaluated to a string. So you cannot give objects. For example, a controller may defined a URL property on the $scope object and you need to get access to that property within the directive. To do that, you can use @ within the directive’s scope property. This is a one-way binding.

If we declare it just with the @, the attribute name would be the same as the property. In case of the second one, the attribute name would have the name “someOtherAttributeName”. Taking the last example, we could use this property and overhand it to our getIp method and use it then in the controller method:

And the directive call would look like this:

See also: https://egghead.io/lessons/angularjs-isolate-scope-attribute-binding

= scope property

The = sets a two-way binding between the isolated scope and the parent scope. Changes in the child scope will be propagated to the parent scope. But also if you need to have a whole object, you have to use this.

Then you could use it like this:

Note, that we do not have here the curly brackets, since this would then evaluate to a string and won’t work as expected.

See also: https://egghead.io/lessons/angularjs-isolate-scope-two-way-binding

& scope property

The & is used to bind to external functions. The & local scope property allows the consumer of a directive to pass in a function that the directive can invoke.

And use it like this:

See also: https://egghead.io/lessons/angularjs-isolate-scope-expression-binding

Conclusion of scope properties

  • @ Used to pass a string value into the directive
  • = Used to create a two-way binding to an object that is passed into the directive
  • & Allows an external function to be passed into the directive and invoked link option

Since we know that with the controller option we cannot manipulate the DOM, we have to use the link option. The link function normally accepts three parameters (although others can be passed in some situations like controller or transclude) including the scope, the element that the directive is associated with, and the attributes of the target element. An example of a directive that handles click, mouseenter, and mouseleave events on an element is shown next:

Which you can use

 

Conclusion of the directive description object

In this chapter, you learned a lot about directives. But that’s good 🙂 See here the conclusion of the explained options.

  • restrict Determines where a directive can be used (as an element, attribute, CSS class, or comment).
  • require: require another controller for your directive
  • scope Used to create a new child scope or an isolate scope.
  • template Defines the content that should be output from the directive. Can include HTML, data
  • binding expressions, and even other directives.
  • templateUrl Provides the path to the template that should be used by the directive. It can optionally
  • contain a DOM element id when templates are defined in <script> tags.
  • controller Used to define the controller that will be associated with the directive template.
  • link Function used for DOM manipulation tasks.

There are some more also the compile option, see the next chapter about compilation. Since most of the time, this is my assumption, you will use the options which we discussed above. The link is when using the compile option the post link code. Within the compile option, you can also define the pre-link function.

Compilation

Let’s say we have our directive, a very simple version of it, which is doing nothing.

This is executed:

  1. directive.compile()
  2. directive.controller()
  3. directive.preLink()
  4. directive.link() or also called directive.postLink()

Even for this simple directive, the whole lifescycle is triggered. Let’s see how the directives are compiled when we have nested directives:

creating-angular-directive-compile

As you see, when we have a more complex hierarchy of directives, first all children are searched and compiled from the deepest to the parent. Then the controller and preLink is executed from parent to the deepest child. The last step is to execute the link or postLink again from the deepest child to the parent. And while b-directive is linked before a-directive, b-directive cannot retrieve information from a-directive. For that, we can create a controller in a-directive, b-directive, c-directive and through that, we are able to use the parents controller to retrieve data even before a is completely linked. Controllers allow directives talk to each other before they get fully initialized.

For optimization purposes the compile is needed. Directives such as ng-repeat compiles repeated elements first, and then just clone. After cloning only controller and link function is called.

 

If we would use all of them, the directive could look like this:

 

If you do not use the pre-link function:

 

Or if you are adding the compile function after you already had the link function, you could do this:

 

If you are using the controller and pos-link:

 

Or simply, just the postLink without anything else:

 

In my next post (http://kusar.ch/2015/11/angular-directive-transclude), I will tell you a little more about transclude in directive.

Angular Directive

Also published on Medium.

Tagged on:

One thought on “Angular Directive

Leave a Reply

Follow

Get every new post on this blog delivered to your Inbox.

Join other followers:

Welcome Damir Kusar

Log in

Lost your password?
%d bloggers like this: