ng-repeat 是如何工作的? [英] How does ng-repeat work?

查看:26
本文介绍了ng-repeat 是如何工作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我剖析了 ng-repeat 并提取了附加的代码块,看到这些包含处理重复算法的逻辑(我想了解它是如何工作的).

I dissected ng-repeat and extracted the code blocks attached, seeing that these comprise the logic that handles the repeating algorithm (which I want to understand how it works).

我有很多问题,但由于它们都是关于 ng-repeat 的内部结构,所以我选择在这里问他们.我看不出有任何理由将它们分成不同的 SO 问题.我已内联标记每个问题所指的代码行.

I have quite a few questions, but since they are all about the internals of ng-repeat I chose to ask them all here. I don't see any reason to separate them into different SO questions. I have marked inline to which line(s) of code each question refers to.

  1. 为什么他们需要确保 trackById 不是原生的 hasOwnProperty 函数?(这就是 assertNotHasOwnProperty 函数所做的,是 Angular 内部 API 的一部分)
  2. 就我的直觉而言,此代码会在转发器中已有的项目上执行,当它必须更新集合时 - 它只是将它们拾取并推送到列表中进行处理,对吗?
  3. 此代码块显然会在转发器集合中查找重复项.但它究竟如何做到这一点超出了我的范围.请解释.
  4. 为什么 Angular 必须在 nextBlockOrder 中同时存储 nextBlockMap 的块对象?
  5. 什么是block.endNodeblock.startNode?
  6. 我认为上述问题的答案将阐明该算法的工作原理,但请解释为什么它必须检查 nextNode 是否已经(已经)'$$NG_REMOVED'?
  7. 这里发生了什么?同样,我假设问题 6 已经为这个问题提供了答案.但还是要指出这一点.
  1. Why do they need to make sure that trackById is not the native hasOwnProperty function? (that's what that assertNotHasOwnProperty function does, part of Angular's internal API)
  2. As far as my intuition go, this code executes on items already in the repeater, when it has to update the collection - it just picks them up and pushes them into the list for processing, right?
  3. This code block obviously looks for duplicates in the repeater collection. But how exactly does it do that is beyond me. Please explain.
  4. Why does Angular have to store the block object both nextBlockMap and in nextBlockOrder?
  5. What are block.endNode and block.startNode?
  6. I assume the answer to the above question will clarify how this algorithm work, but please explain why it has to check if the nextNode has (been) '$$NG_REMOVED'?
  7. What happens here? Again, I assume question 6 will already provide an answer to this one. But still pointing that out.

就像我说的,我在 ng-repeat 中挖掘了我认为与重复机制相关的代码.另外,我确实理解指令的其余部分.所以不用多说,这是代码(来自 v1.2.0):

Like I said, I dug through ng-repeat to find the code I deemed relevant to the repeating mechanism. Plus, I do understand the rest of the directive. So without further ado, here is the code (from v1.2.0):

      length = nextBlockOrder.length = collectionKeys.length;
      for (index = 0; index < length; index++) {
       key = (collection === collectionKeys) ? index : collectionKeys[index];
       value = collection[key];
       trackById = trackByIdFn(key, value, index);

       // question #1
       assertNotHasOwnProperty(trackById, '`track by` id');

       // question #2
       if (lastBlockMap.hasOwnProperty(trackById)) {
         block = lastBlockMap[trackById];
         delete lastBlockMap[trackById];
         nextBlockMap[trackById] = block;
         nextBlockOrder[index] = block;

       // question #3
       } else if (nextBlockMap.hasOwnProperty(trackById)) {
         // restore lastBlockMap
         forEach(nextBlockOrder, function(block) {
           if (block && block.startNode) lastBlockMap[block.id] = block;
         });
         // This is a duplicate and we need to throw an error
         throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}",
                                                                                                                                                expression,       trackById);

       // question #4
       } else {
         // new never before seen block
         nextBlockOrder[index] = { id: trackById };
         nextBlockMap[trackById] = false;
       }
     }


      for (index = 0, length = collectionKeys.length; index < length; index++) {
        key = (collection === collectionKeys) ? index : collectionKeys[index];
        value = collection[key];
        block = nextBlockOrder[index];


        // question #5
        if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode;

        if (block.startNode) {
          // if we have already seen this object, then we need to reuse the
          // associated scope/element
          childScope = block.scope;

          // question #6
          nextNode = previousNode;
          do {
            nextNode = nextNode.nextSibling;
          } while(nextNode && nextNode[NG_REMOVED]);
          if (block.startNode != nextNode) {
            // existing item which got moved
            $animate.move(getBlockElements(block), null, jqLite(previousNode));
          }
          previousNode = block.endNode;

        } else {
          // new item which we don't know about
          childScope = $scope.$new();
        }

        // question #7
        if (!block.startNode) {
          linker(childScope, function(clone) {
            clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
            $animate.enter(clone, null, jqLite(previousNode));
            previousNode = clone;
            block.scope = childScope;
            block.startNode = previousNode && previousNode.endNode ? previousNode.endNode : clone[0];
            block.endNode = clone[clone.length - 1];
            nextBlockMap[block.id] = block;
          });
        }
      }
      lastBlockMap = nextBlockMap;

推荐答案

在修改指令后,我熟悉了 ng-repeater 的代码,并设法回答了我的一些问题.我以粗体突出显示了我自己还无法弄清楚的事情,如果有人能对粗体部分有所了解,我将不胜感激:

After some tinkering with the directive, I became familiar with ng-repeaters code, and managed to answer some of my questions. I highlighted in bold the things I couldn't yet figure out on my own, and would appreciate if anyone could shed some light on the bold parts:

  1. 针对 hasOwnProperty 测试 ID,因为他们使用该方法检查 ID 是否存在于迭代对象中(lastBlockMapnextBlockMap>)(这个过程解释如下).不过,我无法确定这实际上会发生什么情况.
  2. 我的假设是正确的.nextBlockMap 包含将在当前模型更改时嵌入的所有项目.lastBlockMap 包含之前模型更新的所有内容.它用于在集合中查找重复项.
  3. 好的,这个其实很简单.在这个for 循环中,ng-repeat 用来自lastBlockMap 的项目填充nextBlockMap.查看if的顺序,很容易看出,如果在lastBlockMap中找不到该item,但在nextBlockMap中已经存在了(意思是,它已经从 lastBlockMap 复制到那里,因此它的 trackById 在集合中出现了两次) - 它是一个副本.forEach 所做的只是遍历 nextBlockMap(具有 startNode 属性的blocks)中的所有初始化项并将他们的ID推回到lastBlockMap.但是我不明白为什么这是必要的.
  4. 我能找到的将 nextBlockOrder(数组中的所有 trackById)与 nextBlockMap(所有 block 对象在 trackById 散列中),是这一行,它与数组一起工作使其成为一个简单而简单的操作:if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode;.问题 5 和 6 的答案中对此进行了解释:
  5. block.startNodeblock.endNode 是属于重复收集的项目的块中的第一个和最后一个 DOM 节点.因此,这里的这一行将 previousNode 设置为引用转发器中前一项的最后一个 DOM 节点.
  6. previousNode 然后被用作第一个节点,在一个循环中检查当项目被移动或从转发器集合中移除时 DOM 的变化 - 同样,只有在我们不工作的情况下与数组中的第一个块.
  7. 这很容易 - 它初始化块 - 分配 $scopestartNodeendNode 供以后参考,并将所有内容保存在 <代码>nextBlockMap.在克隆元素之后创建的注释可以保证我们总是有一个 endNode.
  1. The ID is tested for hasOwnProperty, because they use that method to check whether the ID is present in the iteration objects (lastBlockMap, nextBlockMap) (this process explained below). I couldn't find out on what scenario this can actually happen, however.
  2. I was correct in my assumption. nextBlockMap contains all items that will be transcluded on the current model change. lastBlockMap contains everything from the previous model update. It used for finding duplicates in the collection.
  3. OK, this one is pretty straightforward actually. In this for loop, ng-repeat fills up nextBlockMap with items from lastBlockMap. Looking at the order of ifs, it's easy to see that if the item cannot be found in lastBlockMap, but it is already present in nextBlockMap (meaning, it was already copied there from lastBlockMap, and therefore its trackById appears twice in the collection) - it's a duplicate. What the forEach does is simply run through all initialized items in nextBlockMap (blocks that have a startNode property) and push their ID back into lastBlockMap. I cannot however understand why this is necessary.
  4. The only reason I could find for separating nextBlockOrder (all trackByIds in an array) from nextBlockMap (all block objects in a trackById hash), is this line, which working with an array makes it an easy and simple operation: if (nextBlockOrder[index - 1]) previousNode = nextBlockOrder[index - 1].endNode;. It is explained in the answers to question 5 and 6:
  5. block.startNode and block.endNode are the first and last DOM nodes in the block belonging to an item in the collected being repeated. Therefore, this line here sets previousNode to reference the last DOM node of the previous item in the repeater.
  6. previousNode is then used as the first node, in a loop that checks how the DOM changed when items have been moved around or removed from the repeater collection - again, only in case we are not working with the first block in the array.
  7. This is easy - it initializes the block - assigning the $scope and startNode and endNode for later reference, and saves everything in nextBlockMap. The comment created right after the cloned element, is there to guarantee we always have an endNode.

这篇关于ng-repeat 是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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