嵌套指令的编译和链接函数的通常执行顺序如下所示
标记
<dir1>
<div dir2="">
</div>
</dir1>
执行顺序
1) compile of directive 1
2) compile of directive 2
3) link of directive 2
4) link of directive 1
假设dir1
restrict
属性设置为'E'
且dir2
restrict
设置为'A'
现在,如果在同一标记中使用ng-repeat
指令,则执行顺序会发生变化
标记
<dir1>
<div ng-repeat="item in items">
<div dir2="">
</div>
</div>
</dir1>
假设在范围上定义items
,执行顺序将更改为
1) compile of directive 1
2) link of directive 1
3) compile of directive 2
4) link of directive 2
Plunker - https://plnkr.co/edit/fRGHS1Bqu3rrY5NW2d97?p=preview
为什么会这样?是因为ng-repeat
将transclude
属性设置为element
。如果是这种情况,为什么要改变dir1
之外ng-repeat
的执行顺序。
非常感谢任何帮助。
答案 0 :(得分:5)
首先,好问题!我曾经使用angular来开发几个webapps,但我从来没有意识到这一点。
这是因为在ngRepeat实施中,谷歌团队使用了
$scope.$watchCollection观察变量并更新元素。(使用其他一些优化。)通过调用$ watchCollection,它调用setTimeout
来异步评估更改。
然后你可以写下你自己的ngRepeat
版本。我们称之为myRepeat
。
//mock ng-repeat : )
app.directive('myRepeat', function ($compile) {
return {
restrict:'A',
transclude: 'element',
priority: 1000,
terminal: true,
$$tlb: true,
compile: function ($element, $attr) {
var expression = $attr.myRepeat;
var ngRepeatEndComment = $compile.$$createComment('end myRepeat', expression);
//parse the ngRepeat expressions.
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
var rhs = match[2]; //this would get items in your example
return function ($scope, $element, $attr, ctrl, $transclude) {
//$watch $scope[rhs] which rhs would be items in your example.
$scope.$watchCollection(rhs, function myRepeatAction(collection) {
$transclude(function(clone, scope) {
clone[clone.length++] = clone; //append element
});
});
}
}
}
});
如果您注释掉watchCollection语句,您将获得第一个示例的输出。你可以用setTimeout替换$ watchCollection来重现相同的日志。
如果我们查看angular.js的源代码,那么callstack就像watchCollection => $watch => $evalAsync => $browser.defer => setTimeout
希望这可以解决您的问题。 :)
This is the fork of your example, with myRepeat implementation. 有关详细信息,请查看github of angular.js。
P.S似乎您的示例的角度版本是1.5.3,所以所有源代码都在 1.5.3。
有关setTimeout的更多详细信息。
基本上你可以将你的例子看作下面的一些函数,
function dir1(callback) {
console.log('compile dir1');
callback();
console.log('link dir1');
}
function dir2() {
console.log('compile dir2');
console.log('link dir2');
}
dir1(dir2);
//compile dir1
//compile dir2
//link dir2
//link dir1
添加ngRepeat
的自定义版本后,代码为
function dir1(callback) {
console.log('compile dir1');
callback();
console.log('link dir1');
}
function dir2() {
console.log('compile dir2');
console.log('link dir2');
}
function myRepeat(callback) {
return function() {
setTimeout(callback, 0);
}
}
dir1(myRepeat(dir2));
//compile dir1
//link dir1
//compile dir2
//link dir2
Sample Code for example 2.看起来很有趣,不是吗?
setTimeout中的回调将在特定秒后调用(在我们的例子中将为0)。
但是在当前代码块完成执行之前不会调用回调,这意味着在我们的例子中将首先输出link dir1
。
1. compile dir1
2. setTimeout(execute after 0 second)
3. link dir1(current block is running, so do this first)
4. compile dir2 (it's free now, invoke the callback)
5. link dir2
这就是我异步的意思。 有关setTimeout的更多详细信息,您可以查看John Resig的How javascript timers work.