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
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.