处理ng-repeat中的重复元素

时间:2013-11-28 16:20:06

标签: javascript angularjs angularjs-ng-repeat

我正在构建一个具有某种"播放列表的应用程序"。这表示带有ng-repeat = "element in playlist"

的ng重复自定义指令

因为我想允许用户在播放列表中重复使用相同的元素两次,所以我尝试使用track by $index添加。

现在,令人困惑的是:当我从播放列表中删除一个元素时(我有一个函数removeElement(index),它基本上包含这样的内容:

$scope.removeElement = function(index){
  $scope.playlist.splice(index, 1);
}

发生了一些奇怪的事情:该元素已从$scope.playlist正确删除,但由于某种原因,该视图未正确更新。最后一个元素似乎已被移除。

此外,我无法正确重新排序数组中的元素。

当我删除track by $index时,这个问题就消失了,所以我认为这是因为当你从数组中删除一个项目时,如果你只是查看索引,那么它就会出现在你面前刚刚删除了最后一个。

行为很奇怪,因为已经正确删除了被盗的内容 - see this plunker

编辑:上述链接已经过修改,可以更好地展示问题并显示我确定的答案。

这个问题也经过了轻微编辑,以便更清楚地了解我的情况。 KayakDave的回答仍然是正确的,但更适合于一系列原语(我原来的plunker特色)。

TL; DR:如何在不牺牲控制位置或正确删除元素的能力的情况下将重复元素放入ng-repeat

2 个答案:

答案 0 :(得分:7)

使用track by的一个重要性能优势是Angular不会触及任何跟踪表达式未更改的DOM元素。对于长ng-repeat列表而言,这是一项巨大的性能提升,也是添加track by的原因之一。

性能优化是你所看到的根源。

当您在$index中使用track by时,您告诉ng-repeat在第一次运行{{}}时将其创建的每个DOM元素与其位置($index)联系起来1}}。

所以颜色样式为红色的元素标记为0,橙色1,黄色2 ......最后是靛蓝5。

删除颜色时,Angular会查看您告诉它要跟踪的索引并看到您的索引#5更长(因为您的列表比以前更短)。因此,它删除了标记为5-的DOM元素,其具有“indigo”的颜色样式。你仍然有一个索引#2,所以黄色的元素保持不变。

令人困惑的是,由于数据绑定,DOM元素内的文本确实会更新。因此,当您删除“黄色”时,带有黄色的DOM元素会将文本设置为“绿色”。

简而言之,您所看到的是 ng-repeat,因为它的跟踪值(2)仍然存在,但数据绑定已更新该元素内的文本,因此保留了黄色样式的DOM元素,因为它的跟踪值(2)仍然存在。

要添加具有相同颜色的多个条目,您需要为ng-repeat添加用于每个条目的唯一标识符。一种方法是为每个条目使用键值对,其中键是您的唯一标识符。像这样:

track by

然后$scope.colorlist = {1:'red', 2:'orange',3:'yellow',4:'green',5:'blue',6:'indigo',7:'yellow'}; 密钥如下:

track by

并确保使用该密钥进行删除选择:

<color-block ng-repeat="(key, color) in colorlist track by key" color="{{color}}" ng-transclude>
    {{color}}
</color-block>

现在,颜色样式为黄色的DOM元素与您为“黄色”数组元素指定的键相关联。因此,当您删除“黄色”时,<option value="{{key}}" ng-repeat="(key,color) in colorlist">{{color}}</option> 将删除正确的DOM元素,一切正常。

您可以在此处查看:http://plnkr.co/edit/cFaU8WIjliRjPI6LInZ0?p=preview

答案 1 :(得分:5)

我想为这个问题添加另一个答案,因为我发现了一个更简单的解决方案。

ng-repeat有一个重要的section of the documentation很容易错过,特别是在欺骗错误上。

它声明:

  

默认情况下,集合按引用键入

阅读之后,解决方案显而易见 - 因为我没有处理原语(是的,但是,这是一个过度简化)我需要复制< / strong>复制对象并将其副本添加到数组中。这意味着当您通过$ index 删除跟踪时,一切都按预期工作,只是让默认行为接管。

Angular使这一点变得特别容易,因为jqlite有一个.copy。方法

以下是我所说的 demonstrated in a plunker