How to execute parent directive before child directive?
angularjs, angularjs-directive, javascript
Solution
Reasoning
`postLink()` is executed in reverse order, which means the child directive's `postLink()` will be called before the parent's (i.e. depth first). For some reason, this is the default behavior (`link()` actually refers to `postLink()`). Luckily we also have `preLink()`, which works the other way around - we can utilize that to our benefit.
To illustrate this - the following snippet of code:
app.directive('parent', function($log) {
return {
restrict: 'E',
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
$log.info('parent pre');
},
post: function postLink(scope, iElement, iAttrs, controller) {
$log.info('parent post');
}
}
}
};
});
app.directive('child', function($log) {
return {
restrict: 'E',
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
$log.info('child pre');
},
post: function postLink(scope, iElement, iAttrs, controller) {
$log.info('child post');
}
}
}
};
});
… will output the following:
> parent pre
> child pre
> child post
> parent post
See it live on plunker.
Solution
If we want the parent directive's logic to be performed before the child's, we will explicitly use `preLink()`:
function SortableWidgetsDirective() {
return {
restrict: 'A',
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
iElement.find(".widget header").append($("<div class='widget-controls'></div>"));
iElement.sortable({});
},
post: angular.noop
}
}
};
}
function CloneableWidgetDirective() {
return {
restrict: 'A',
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
iElement.find("header .widget-controls").append($("<div class='clone-handle'></div>"));
},
post: angular.noop
}
}
};
}
References
- `$compile` service on the AngularJS docs.
Post Scriptum
You are correct, by the way - `priority` is meant for use with directives that share the same element.
`angular.noop` is just an empty method that returns nothing. If you still want to use the `postLink()` functions, just place the function declaration instead, as you would normally do, i.e.:
post: function postLink(scope, iElement, iAttrs, controller) { ... }
Be ware of the use of `templateUrl`, as “ Because the template loading is asynchronous the compilation/linking is suspended until the template is loaded ” [source]. As a result, the order of execution will be disrupted. You can remedy this by including the template inlined in the `template` property instead.
Problem
I'm looking to write two angular directives, a parent and a child directive, to create sortable and cloneable widgets. The intended markup is: ``` <div class="widget-container" data-sortable-widgets> <section class="widget" data-cloneable-widget></section> <div> ``` However, the child directive seems to execute before the parent, before a certain element is available (its added by the parent): ``` function SortableWidgetsDirective() { return { priority: 200, restrict: 'A', link: function ($scope, element, attrs) { element.find(".widget header").append($("<div class='widget-controls'></div>")); element.sortable({ }); } }; } function CloneableWidgetDirective() { return { priority: 100, restrict: 'A', link: function ($scope, element, attrs) { // This directive wrongfully executes first so widget-controls is no available element.find("header .widget-controls").append($("<div class='clone-handle'></div>")); } }; } ``` As you can see i tried setting priority but I think because they're on different elements, it does not work. How can I make the parent execute first?