为什么 post 链接函数会在被嵌入的子链接函数之前执行? [英] Why is the post link function executed before the transcluded child link functions?
问题描述
AngularJS 中 (pre/post)link 函数的时序在 )
angular.module('someApp', []).directive('dropright', function() {返回 {限制:'E',转置:'真实',控制器:函数($scope,$element,$attrs){console.info('controller - dropright');$scope.col1Tab = [];$scope.col2Tab = [];this.addCol1Item = 函数(el){console.log('(col1Tab 推送)');$scope.col1Tab.push(el);};this.addCol2Item = 函数(el){console.log('(col2Tab 推送)');$scope.col2Tab.push(el);};},关联: {帖子:功能(范围,元素,属性){console.info('post-link - dropright');//这里,我想移动#col1-el 的一些元素//进入#col2-el}},templateUrl: 'dropright-tpl.html'};}).directive('col1Item', function($interpolate) {返回 {要求:'^dropright',限制:'E',转置:真实,控制器:函数(){console.log('-- 控制器 - col1Item');},关联: {帖子:功能(范围,元素,属性,droprightCtrl){console.log('-- post-link - col1Item');droprightCtrl.addCol1Item(element.children()[0]);}},templateUrl: 'col1-tpl.html'};}).directive('col2Item', function() {var 指令定义对象 = {要求:'^dropright',限制:'E',转置:真实,控制器:函数(){console.log('---- 控制器 - col2Item');},关联: {帖子:功能(范围,元素,属性,droprightCtrl){console.log('---- post-link - col2Item');droprightCtrl.addCol2Item(element.children()[0]);}},templateUrl: 'col2-tpl.html'};返回指令定义对象;});
在使用嵌入时,是否有任何干净的方法在指令的所有链接函数之后执行指令的链接函数?
这是我的理论 - 导致序列问题的不是嵌入方面,而是模板是 templateUrl.模板需要在 post link 函数对其进行操作之前解析 - 因此我们说 post link 函数可以安全地进行 DOM 操作.虽然我们为所有 3 个模板都得到了 304,但我们必须阅读它们并最终解决模板承诺.
我用模板而不是 templateUrl 创建了一个 plunker 来证明推论.我有很多次热刷新/plunker 停止/运行,但我总是在最后得到 link - dropright
.
我不会假装完全理解 compile.js 代码.然而它确实出现在compileTemplateUrl
函数 $http.success()
解析模板,然后在成功时调用 applyDirectivesToNode
函数,传入 postLinkFn
.
https://github.com/angular/angular.js/blob/master/src/ng/compile.js
The timing of (pre/post)link functions in AngularJS are well defined in the documentation
Pre-linking function
Executed before the child elements are linked. Not safe to do DOM transformation since the compiler linking function will fail to locate the correct elements for linking.
Post-linking function
Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.
and this blog post clearly illustrates this expected order.
But this order does not seem to apply when using ng-transclude
and nested directives.
Here is an example for a dropright element (See the Plunkr)
<!-- index.html -->
<dropright>
<col1-item name="a">
<col2-item>1</col2-item>
<col2-item>2</col2-item>
</col1-item>
<col1-item name="b">
...
</col1-item>
</dropright>
// dropright-template.html
<div id="col1-el" ng-transclude></div>
<div id="col2-el">
<!-- Only angularJS will put elements in there -->
</div>
// col1-item-template.html
<p ng-transclude></p>
// col2-item-template.html
<div ng-transclude></div>
The dropright looks like
The directives write a log in the console when their link and controller functions are called. It usually displays:
But sometimes (after few refreshes), the order is not as expected:
The dropright post-link function is executed before the post-link function of its children.
It may be because, in my particular case, I am calling the dropright controller in the children's directives (See the Plunkr)
angular.module('someApp', [])
.directive('dropright', function() {
return {
restrict: 'E',
transclude: 'true',
controller: function($scope, $element, $attrs) {
console.info('controller - dropright');
$scope.col1Tab = [];
$scope.col2Tab = [];
this.addCol1Item = function(el) {
console.log('(col1Tab pushed)');
$scope.col1Tab.push(el);
};
this.addCol2Item = function(el) {
console.log('(col2Tab pushed)');
$scope.col2Tab.push(el);
};
},
link: {
post: function(scope, element, attrs) {
console.info('post-link - dropright');
// Here, I want to move some of the elements of #col1-el
// into #col2-el
}
},
templateUrl: 'dropright-tpl.html'
};
})
.directive('col1Item', function($interpolate) {
return {
require: '^dropright',
restrict: 'E',
transclude: true,
controller: function() {
console.log('-- controller - col1Item');
},
link: {
post: function(scope, element, attrs, droprightCtrl) {
console.log('-- post-link - col1Item');
droprightCtrl.addCol1Item(element.children()[0]);
}
},
templateUrl: 'col1-tpl.html'
};
})
.directive('col2Item', function() {
var directiveDefinitionObject = {
require: '^dropright',
restrict: 'E',
transclude: true,
controller: function() {
console.log('---- controller - col2Item');
},
link: {
post: function(scope, element, attrs, droprightCtrl) {
console.log('---- post-link - col2Item');
droprightCtrl.addCol2Item(element.children()[0]);
}
},
templateUrl: 'col2-tpl.html'
};
return directiveDefinitionObject;
});
Is there any clean way to execute the link function of a directive after all the link functions of its children while using transclusion?
This is my theory - its not the transclude aspect that is causing the sequence issue but rather the template being a templateUrl. The template needs to be resolved before the post link function get to act on it - hence we say post link function is safe to do DOM manipulation. While we are getting 304s for all the 3 templates - we do have to read them and it ultimately resolves the template promise.
I created a plunker with template instead of templateUrl to prove the corollary. I have hot refresh/plunker Stop/Run many times but I always get link - dropright
at the end.
Plunker with template instead of templateUrl
I don't pretend to understand the compile.js code fully. However it does appear that in
compileTemplateUrl
function $http.success()
resolves the template and then on success the applyDirectivesToNode
function is called passing in postLinkFn
.
https://github.com/angular/angular.js/blob/master/src/ng/compile.js
这篇关于为什么 post 链接函数会在被嵌入的子链接函数之前执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!