如何NG-重复的工作? [英] How does ng-repeat work?

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

问题描述

我解剖NG-重复提取附code块,看到这些包括处理在重复算法(这是我想了解它是如何工作)的逻辑。

我有好几个问题,但因为都是有关的内部NG-重复我选择了让他们都在这里。我看不出有任何理由将它们分成不同的做题。我标志着行内以code的哪一行(S)每个问题指的是。


  1. 为什么他们需要确保 trackById 不是本机的hasOwnProperty 功能? (这是该 assertNotHasOwnProperty 函数做什么,角的内部API的一部分)

  2. 至于我的直觉走,这code对项目执行已经在转发时,它必须更新集合 - 它只是挑选他们和他们推到列表进行处理,对
  3. 这code座显然查找中继集合中重复。但是,我是无法理解究竟如何会这样。请解释一下。

  4. 为什么角已存储的块对象既 nextBlockMap nextBlockOrder

  5. 什么是 block.endNode block.startNode

  6. 我假设的答案,上述问题将阐明如何算法的工作,但请解释为什么它来检查 nextNode 有(​​过)$$ NG_REMOVED

  7. 在这里会发生什么?同样,我认为问题6将已经提供了一个答案之一。但还是指出了这一点。

就像我说的,我通过NG-重复挖找到code我认为相关的重复机制。另外,我明白该指令的其余部分。因此,事不宜迟,这里是code(从V1.2.0):

 长度= nextBlockOrder.length = collectionKeys.length;
      对于(指数= 0;指数 - LT;长度指数++){
       键=(收集=== collectionKeys)?指数:collectionKeys [指数]
       值=收集[关键]
       trackById = trackByIdFn(键,值,指数);       // 问题1
       assertNotHasOwnProperty(trackById,''跟踪by` ID');       //问题#2
       如果(lastBlockMap.hasOwnProperty(trackById)){
         块= lastBlockMap [trackById]
         删除lastBlockMap [trackById]
         nextBlockMap [trackById] =块;
         nextBlockOrder [指数] =块;       //问题#3
       }否则如果(nextBlockMap.hasOwnProperty(trackById)){
         //恢复lastBlockMap
         的forEach(nextBlockOrder,功能(块){
           如果(块放;&安培; block.startNode)lastBlockMap [block.id] =块;
         });
         //这是一个重复的,我们需要抛出一个错误
         扔ngRepeatMinErr('愚弄',在一个中继器不允许重复按曲目前pression用于指定唯一键转发:{0},重复键:{1},
                                                                                                                                                前pression,trackById);       //问题#4
       }其他{
         //新前所未见块
         nextBlockOrder [指数] = {ID:trackById};
         nextBlockMap [trackById] = FALSE;
       }
     }
      对于(指数= 0,长度= collectionKeys.length;指数<长度指数++){
        键=(收集=== collectionKeys)?指数:collectionKeys [指数]
        值=收集[关键]
        块= nextBlockOrder [指数]
        //问题#5
        如果(nextBlockOrder [指数 - 1])$ ​​P $ pviousNode = nextBlockOrder [指数 - 1] .endNode;        如果(block.startNode){
          //如果我们已经看到了这个对象,那么我们就需要重新使用
          //相关范围/元
          childScope = block.scope;          //问题#6
          nextNode = previousNode;
          做{
            nextNode = nextNode.nextSibling;
          }而(nextNode&安培;&安培; nextNode [NG_REMOVED]);
          如果(block.startNode!= nextNode){
            这得到了感动//现有项目
            $ animate.move(getBlockElements(块),空,jqLit​​e(previousNode));
          }
          previousNode = block.endNode;        }其他{
          //这是我们不知道的新项目
          。childScope = $范围美元的新();
        }        //问题#7
        如果(!block.startNode){
          连接器(childScope,功能(克隆){
            克隆[clone.length ++] = document.createComment(结束ngRepeat:'+ EX pression +'');
            $ animate.enter(克隆,空,jqLit​​e(previousNode));
            previousNode =克隆;
            block.scope = childScope;
            block.startNode = previousNode&放大器;&安培; previousNode.endNode? previousNode.endNode:克隆[0];
            block.endNode =克隆[clone.length - 1];
            nextBlockMap [block.id] =块;
          });
        }
      }
      lastBlockMap = nextBlockMap;


解决方案

该指令一些修修补补之后,我渐渐熟悉了 NG-转发取值code,并成功地回答了我的一些问题。我强调粗体的事情我还不能找出我自己,并且将AP preciate如果任何人都可以摆脱对粗体部分一些轻:


  1. ID是的hasOwnProperty 测试,因为它们使用的方法来检查ID是否present在迭代对象( lastBlockMap nextBlockMap )(这个过程解释如下)。 我无法找出什么情况下这实际上可以发生,但是。

  2. 我在我的假设是正确的。 nextBlockMap 包含将在现款车型变化来transcluded的所有项目。 lastBlockMap 包含一切从previous模型更新。它用于收集在寻找重复。

  3. 确定,这个人是pretty简单实际。在这种循环, NG-重复填补了 nextBlockMap lastBlockMap 项目。纵观如果 S中的的顺序,可以很容易地看到,如果项目不能在 lastBlockMap 中找到,但它已经是present在 nextBlockMap (意思是,它已经从 lastBlockMap 复制存在,因此,其 trackById 集合中出现两次) - 这是一个重复的。什么的forEach 不只是通过 nextBlockMap S中的有一个的StartNode 属性),推动的的ID 的回 lastBlockMap 我却无法理解为什么这是必要的。

  4. 我能找到从<$ C分离 nextBlockOrder (所有 trackById S IN数组)的唯一原因$ C> nextBlockMap (所有 trackById 散列对象),这是行,它使用数组使得它容易和简单的操作:如果(nextBlockOrder [指数 - 1])$ ​​p $ pviousNode = nextBlockOrder [指数 - 1] .endNode; 。它在解释回答问题5和6:

  5. block.startNode block.endNode 在块中的第一和最后一个DOM节点属于一个项在所收集的被重复。因此,这条线在这里设置 previousNode 引用previous项目的最后一个DOM节点的转发。

  6. previousNode 然后作为第一个节点,在一个循环来检查时,项目已移至周围或从中继集合中删除的DOM如何改变 - 再次,只有在情况下,我们不与所述阵列中的第一个块的工作。

  7. 这是很容易 - 它初始化块 - 分配 $范围的StartNode 终端节点供日后参考,并在 nextBlockMap 。克隆的元素之后创建的评论,有没有保证,我们总是有一个终端节点

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).

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. 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.

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;

解决方案

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. 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-重复的工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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