【问题标题】:Handling duplicate elements in an ng-repeat处理 ng-repeat 中的重复元素
【发布时间】:2013-12-13 03:58:11
【问题描述】:

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

的 ng-repeated 自定义指令

因为我想允许用户在播放列表中重复使用相同的元素两次,所以我尝试使用 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 中放置重复元素而不牺牲控制其位置或正确删除元素的能力?

【问题讨论】:

    标签: javascript angularjs angularjs-ng-repeat


    【解决方案1】:

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

    性能优化是您所见的根本原因。

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

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

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

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

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

    要添加具有相同颜色的多个条目您需要为每个条目添加一个唯一标识符,用于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>
    

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

    <option value="{{key}}" ng-repeat="(key,color) in colorlist">{{color}}</option>
    

    现在颜色样式为黄色的 DOM 元素与您为“黄色”数组元素指定的键相关联。因此,当您删除“黄色”时,ng-repeat 将删除正确的 DOM 元素,一切正常。

    你可以在这里看到它的工作原理:http://plnkr.co/edit/cFaU8WIjliRjPI6LInZ0?p=preview

    【讨论】:

    • 谢谢,这是一个非常彻底的解释。按颜色跟踪适用于我的 plunker,但输入 track by $index 的原因是我可以在列表中包含重复的元素。 track by color 意味着我不能拥有["red", "blue", "red"] - 有替代品吗? 编辑我意识到我应该在我的 plunkr 列表中包含一个副本,以使我的问题更清楚;)
    • 你可以。我更新了答案以涵盖处理多个元素(使用唯一标识符)。
    【解决方案2】:

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

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

    它说:

    默认情况下,集合是通过引用键控

    阅读本文后,解决方案很明显 - 因为我没有处理原语(是的,plunker 是,但这是一个过度简化)我需要复制 复制对象并将其副本添加到数组中。这意味着当您按 $index 删除曲目并让默认行为接管时,一切都会按预期进行。

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

    这就是我要说的demonstrated in a plunker

    【讨论】:

    • 你能发布一个更新的 plunker 来显示这个吗?看起来这会导致同样的问题——因为问题似乎不是集合/对象键,而是与 $index 相关,我希望它仍然不匹配。所以我很想知道我错过了什么。这将有助于其他与您发布相同问题的人更好地了解您的解决方案。
    • 你说得对 - 我在这里忽略了说这是使用 track by $index 的替代方法。基元需要track by $index,但对于我帖子中的播放列表示例,复制您想要包含的曲目两次比按索引跟踪更有意义(因为重新排序很可能需要)。
    • 现在说得通了。感谢更新。如果你有时间,你也可以调整你的问题——现在你将你的问题总结为“如果你使用 $index 的跟踪,你如何正确地从 ng-repeat 数组中删除一个元素?”我认为你现在正在回答一个更广泛的问题。就这样,这一切都有助于后代:)
    • 绝对 - 我现在将对其进行编辑。我用 plunker 表现得更好。
    猜你喜欢
    • 1970-01-01
    • 2014-07-11
    • 2013-04-05
    • 2014-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-26
    • 1970-01-01
    相关资源
    最近更新 更多