为什么ng-repeat更改链接函数执行的顺序 [英] why ng-repeat changes order of link function execution

查看:97
本文介绍了为什么ng-repeat更改链接函数执行的顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在嵌套指令上执行编译和链接函数的通常顺序如下

The usual order of execution of compile and link function on nested directives is as below

标记

<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

假设dir1restrict属性设置为'E',而dir2restrict属性设置为'A'

Assuming dir1 has restrict property set to 'E' and dir2 has restrict set to 'A'

现在,如果您在同一标记中使用ng-repeat指令,则执行顺序会更改

Now if you use a ng-repeat directive in the same markup, the order of execution changes

标记

<dir1>
  <div ng-repeat="item in items">
    <div dir2="">
    </div>
  </div>
</dir1>

假设items是在作用域上定义的,则执行顺序更改为

assuming items is defined on scope, the order of execution changes to

1) compile of directive 1
2) link of directive 1
3) compile of directive 2
4) link of directive 2

柱塞- https://plnkr.co/edit/fRGHS1Bqu3rrY5NW2d97?p=preview

为什么会这样?是因为ng-repeattransclude属性设置为element.如果是这样,为什么要更改ng-repeat之外的dir1的执行顺序.

Why does this happen? Is is because ng-repeat has transclude property set to element. If that is the case, why should it alter the order of execution of dir1 which is outside ng-repeat.

任何帮助将不胜感激.

推荐答案

首先,很好的问题!我曾经使用angular来开发多个Web应用程序,但我从未意识到这一点.

First of all, nice question! I used to use angular to develop several webapps, but I never realized this.

这是因为在 ngRepeat 实施,Google团队使用 $ scope.$ watchCollection (通过一些其他优化.)通过调用$ watchCollection,它调用setTimeout异步评估更改.

This is because inside of ngRepeat implementation, the google team use the $scope.$watchCollection to watch the variables and update the element.(With some other optimizations.) With invoking the $watchCollection, it calls the setTimeout to eval the changes asynchronously.

然后,您可以写下您自己的ngRepeat版本.我们称之为myRepeat.

Then you can write down your own version of ngRepeat. Let's call it 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来重现相同的日志.

If you comment out the watchCollection statement, you will get the output of your first example. And you can replace the $watchCollection with setTimeout to reproduce the same logs also.

如果我们查看angular.js的源代码,则调用堆栈将类似于watchCollection => $watch => $evalAsync => $browser.defer => setTimeout

If we look into the source code of angular.js, the callstack would be like watchCollection => $watch => $evalAsync => $browser.defer => setTimeout

$ watch源代码.

$ browser.defer源代码

希望这可以解决您的问题. :)

Hope this would solve your problem. : )

这是使用myRepeat实现的示例示例. 有关更多详细信息,您可以检查angular.js的 github

This is the fork of your example, with myRepeat implementation. For more detail, you can check the github of angular.js.

P.S似乎示例的角度版本为1.5.3,因此所有源代码都位于 1.5.3.

P.S Seems the angular version of your example is 1.5.3, so all the source code would be in 1.5.3.

有关setTimeout的更多详细信息.

More details about the setTimeout.

基本上,您可以将示例视为以下某些功能,

Basically you can regard your example as some functions below,

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后,代码应为

And after added the custom version of ngRepeat, the code would be,

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

示例代码2.看起来很有趣,不是吗?

Sample Code for example 2. Seems pretty funny, isn't it?

setTimeout中的回调将在特定的秒后调用(在本例中为0).

The callback in setTimeout would be invoked after specific seconds, (would be 0 in our case).

但是在当前代码块完成执行之前,不会调用回调,这意味着在我们的示例中,将首先输出link dir1.

But the callback would not be invoked until the current block of code completes its execution, which means in our case will output the link dir1 first.

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的 javascript计时器的工作原理.

So that's what I mean asynchronously. For more details about setTimeout, you can check John Resig's How javascript timers work.

这篇关于为什么ng-repeat更改链接函数执行的顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆