Yat*_*era 5 angularjs angularjs-directive angularjs-ng-repeat angularjs-compile
在嵌套指令上执行编译和链接函数的通常顺序如下
标记
<dir1>
<div dir2="">
</div>
</dir1>
Run Code Online (Sandbox Code Playgroud)
执行顺序
1) compile of directive 1
2) compile of directive 2
3) link of directive 2
4) link of directive 1
Run Code Online (Sandbox Code Playgroud)
假设dir1已将restrict属性设置为'E'并dir2已restrict设置为'A'
现在,如果ng-repeat在同一标记中使用指令,则执行顺序会发生变化
标记
<dir1>
<div ng-repeat="item in items">
<div dir2="">
</div>
</div>
</dir1>
Run Code Online (Sandbox Code Playgroud)
假设items是在范围上定义的,执行顺序变为
1) compile of directive 1
2) link of directive 1
3) compile of directive 2
4) link of directive 2
Run Code Online (Sandbox Code Playgroud)
Plunker - https://plnkr.co/edit/fRGHS1Bqu3rrY5NW2d97?p=preview
为什么会这样?是因为ng-repeat已经transclude属性设置为element.如果是这种情况,为什么要改变执行顺序在dir1外面ng-repeat.
任何帮助将非常感激.
首先,好问题!我曾经使用angular来开发几个webapps,但我从来没有意识到这一点.
这是因为在ngRepeat实现中,Google团队使用
$ scope.$ watchCollection来监视变量并更新元素.(使用其他一些优化.)通过调用$ watchCollection,它setTimeout会异步调用eval 变量.
然后你可以写下你自己的版本 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
});
});
}
}
}
});
Run Code Online (Sandbox Code Playgroud)
如果注释掉watchCollection语句,您将获得第一个示例的输出.你可以用setTimeout替换$ watchCollection来重现相同的日志.
如果我们查看angular.js的源代码,那么callstack会是这样的 watchCollection => $watch => $evalAsync => $browser.defer => setTimeout
希望这能解决你的问题.:)
这是您的示例的分支,具有myRepeat实现. 有关更多详细信息,您可以检查angular.js的github.
PS似乎你的例子的角度版本是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
Run Code Online (Sandbox Code Playgroud)
添加自定义版本后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
Run Code Online (Sandbox Code Playgroud)
示例代码例如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
Run Code Online (Sandbox Code Playgroud)
这就是我所说的异步.有关setTimeout的更多详细信息,您可以查看John Resig的javascript计时器如何工作.
| 归档时间: |
|
| 查看次数: |
569 次 |
| 最近记录: |