我有很多问题,但由于它们都是关于ng-repeat的内部结构,所以我选择在这里问问它们.我认为没有理由将它们分成不同的SO问题.我已经在内联标记了每个问题引用的代码行.
>为什么他们需要确保trackById不是本机hasOwnProperty函数? (这就是assertNotHasOwnProperty函数所做的,是Angular内部API的一部分)
>就我的直觉而言,这段代码会对已经在转发器中的项目执行,当它必须更新集合时 – 它只是将它们拾取并将它们推入列表进行处理,对吧?
>此代码块显然在转发器集合中查找重复项.但它究竟是如何做到这一点超出了我.请解释.
>为什么Angular必须将块对象存储在nextBlockMap和nextBlockOrder中?
>什么是block.endNode和block.startNode?
>我假设上述问题的答案将阐明该算法的工作原理,但请解释为什么必须检查nextNode是否已经’$$NG_REMOVED’?
>这里发生了什么?同样,我认为问题6已经为这一个提供了答案.但仍然指出了这一点.
就像我说的那样,我挖掘ng-repeat来找到我认为与重复机制相关的代码.另外,我完全理解指令的其余部分.所以不用多说,这里是代码(来自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,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;
>为hasOwnProperty测试ID,因为它们使用该方法检查迭代对象中是否存在ID(lastBlockMap,nextBlockMap)(此过程如下所述).然而,我无法找出实际发生的情况.>我的假设是正确的. nextBlockMap包含将在当前模型更改中转换的所有项目. lastBlockMap包含先前模型更新的所有内容.它用于在集合中查找重复项.>好的,这个实际上很简单.在这个for循环中,ng-repeat用lastBlockMap中的项填充nextBlockMap.查看ifs的顺序,很容易看出如果在lastBlockMap中找不到该项,但它已经存在于nextBlockMap中(意思是,它已经从lastBlockMap复制到那里,因此它的trackById在集合中出现两次) – 这是重复的. forEach执行的操作只是遍历nextBlockMap中的所有初始化项(具有startNode属性的块)并将其ID重新推送到lastBlockMap.但我无法理解为什么这是必要的.>我可以找到将nextBlockOrder(数组中的所有trackByIds)与nextBlockMap(trackById哈希中的所有块对象)分开的唯一原因是这一行,它使用数组使其成为一个简单而简单的操作:if(nextBlockOrder [ index – 1])prevIoUsNode = nextBlockOrder [index – 1] .endNode;.在问题5和6的答案中对此进行了解释:> block.startNode和block.endNode是块中属于被重复收集的项目的第一个和最后一个DOM节点.因此,此行在此处将prevIoUsNode设置为引用转发器中上一项的最后一个DOM节点.>然后将prevIoUsNode用作第一个节点,在一个循环中检查DOM在转移项目或从转发器集合中移除时如何更改 – 同样,仅在我们不使用数组中的第一个块的情况下.>这很简单 – 它初始化块 – 分配$scope和startNode以及endNode供以后引用,并将所有内容保存在nextBlockMap中.在克隆元素之后创建的注释,是为了保证我们始终有一个endNode.