Directive Compilation in AngularJS – step-by-step

One of AngularJS core features is the possibility to teach the browser new HTML syntax. Its built-in HTML compiler allows developer to define new behavior for any HTML elements or their attributes. Creators have called these behavior extensions directives. To make better use of directives API and give them super powers we need to get an in-depth understanding of the compilation mechanism.


Directive basic idea

Directive is a way to take HTML as it is and make it a bit more functional using JavaScript:

  • (x)HTML(5) = custom tags/attributes
  • JavaScript = custom functionality
  • AngularJS Directive = HTML + JS

To achieve this Angular first traverses HTML in search of recognisable custom elements or tags and then links them with predefined JavaScript functionality. Those 2 steps are named Compile phase and Link phase and together they compose Compilation process.

Compilation phases

Compiler is an Angular service which traverses the DOM looking for attributes. The compilation process happens in two phases.

  1. Compile: traverse the DOM and collect all of the directives. The result is a linking function.
  2. Link: combine the directives with a scope and produce a live view. Any changes in the scope model are reflected in the view, and any user interactions with the view are reflected in the scope model. This makes the scope model the single source of truth.

Some directives such as ng-repeat clone DOM elements once for each item in the collection. Having a compile and link phases improves the performance since the cloned template only needs to be compiled once, and then linked once for each clone instance.

AngularJS – Developer Guide: HTML Compiler

So basically it seems to look like this:

Compilation Steps

Compilation Steps

If we take a deeper dive inside, we will see there’s more:

Compilation Steps

Compilation Steps

The linking phase is actually divided – it takes two steps to link our custom HTML tag with its JS functionality.

Pre and Post link functions do exactly the same – the difference is in the order in which they are executed. Pre goes first from root level tags to the deepest nested nodes (and from high priority directives to low priority ones) and then Post links are invoked in the reverse order (the in-depth explanation can be found in the following paragraphs). This phasing gives us some more possibilities than just binding behavior to element. However this is not the full picture yet. There are some more pieces that make AngularJS Directive a real Swiss Army Knife:

Compilation Steps

Compilation Steps

Dashed lines show the relationship between directive definition parts. So:

  • compile function needs a template to be resolved (if it’s defined) before execution
  • any link functions are invoked after compile function execution (if it’s defined)
  • controller is invoked just before linking phase – it’s injected as a dependency into the link functions

Moreover you can use any of this elements alone – the directive can use only pre-link function, or just set the template, or even use controller alone.

Check a fiddle here: jsfiddle.net/ulfryk/q41vqr3o


Compile many directives on single element

Compilation order on one DOM element depends on priority of each directive attached to that element. If not defined, priority defaults to 0. Let’s look at built-in AngularJS directives priority examples:

Directive Priority
ng-if 600
ng-include 400
ng-repeat 1000
ng-controller 500

Rules defining compilation order for many directives on single element can be summarized in 5 points:

  • compile and pre-link by priority
  • post-link reverse to priority
  • template just before compile
  • controller just before linking
  • terminal blocks ones with lower priority

And if we had 3 directives with priorities 100, 0 and -100 the compilation timeline may look like this:

Multi-Directive Tag Compilation

Multi-Directive Tag Compilation

ALL controllers are instantiated in the beginning of linking phase so they are available for any linking function. Linking phase starts only when ALL compile functions were executed (templates are resolved in the beginning of compile phase). Any terminal directive brakes chain – no directives with lower priority will be compiled for that element.

And the demo fiddle: jsfiddle.net/ulfryk/5fwd20gj


Compile many directives on nested elements

Compilation order of nested directives is similar to that based on priority – the deeper the element nested, the later it is compiled. The main difference is that controllers for each level are instantiated just before it’s pre-linking phase – not before all linking in a tree. And in the following order:

  • template and compile from ancestors to deepest descendants,
  • then pre-links preceeded by controllers from ancestors to descendants,
  • finally post-links back from descendants to the top.

Let’s consider three nested divs – <div><div><div></div></div></div>:

Nested Directives Compilation

Nested Directives Compilation

And the demo fiddle: jsfiddle.net/ulfryk/g3hLyzt9


Conclusion

The mechanism described above gives a developer a lot of possibilities to link directives together and establish communication between own components without the overuse of events. It also facilitates use of third-party UI components (like jQuery plugins).


Further Reading

Here’s some additional resources you can read to further extend your familiarity with AngularJS directives: