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 ¶
- (x)HTML(5) = custom tags/attributes
- AngularJS Directive = HTML + JS
Compilation phases ¶
Compiler is an Angular service which traverses the DOM looking for attributes. The compilation process happens in two phases.
- Compile: traverse the DOM and collect all of the directives. The result is a linking function.
- 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.
So basically it seems to look like this:
If we take a deeper dive inside, we will see there’s more:
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:
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:
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:
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 –
And the demo fiddle: jsfiddle.net/ulfryk/g3hLyzt9
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: