Angularjs:buggy双向数据绑定

时间:2014-06-19 08:20:19

标签: javascript angularjs angularjs-directive angularjs-scope

我正在尝试使用指令创建递归网格布局。

  1. 有一个父容器(appliedgrids),其中包含网格数组。
  2. 网格中包含列数组。
  3. 每列有两个属性:span(列宽)和data(列内数据)
  4. 每个列数据再次包含网格或小部件。如果它包含grid,那么它会对grid指令进行递归调用。
  5. 我的问题是当我使用其中的删除按钮删除网格时 - 它会从应用网格容器中删除,但双向数据绑定不能正常工作。代替当前网格,最后一个网格将从UI中删除。

    Link- http://plnkr.co/edit/DzKIHKvJdLoZiYY3jgDx?p=preview

    重现的步骤: 1)单击第一个网格上的删除按钮,您将看到代替第一个网格,第二个网格被删除。 applygrid的json数据包含其中的第二个网格。因此角度的双向绑定并不像它应该的那样工作。

2 个答案:

答案 0 :(得分:2)

我在之前的回答中做了一点思考,结果证明这不正确。

首先,请勿使用track by $index。在你的情况下没有任何意义。 track byng-repeat的优化,用于关联数组中的(可能是新的)对象,这些对象是"业务方面的"等于数组中的旧对象,以便它重用范围和DOM元素,以尽量减少DOM操作。也就是说:如果你给ng-repeat一个提示,说明新数组中的新对象是"等于"对于旧数组中的旧对象,它将重用其范围并跳跃使新对象与旧对象相比没有显着差异,将会触发较少$watch个回调,并且将发生较少的DOM更新。

你的实际问题是你是静静的"或者"一次性"使用以下语句绑定数据:

$scope.gridIndex = $parse($attrs.gridIndex)($scope);
$scope.gridValues=$parse($attrs.appliedgrid)($scope);
$scope.gridParent=$parse($attrs.appliedgrids)($scope);

确实从数组中删除了第一个grid项,但ng-repeat未删除其范围和DOM元素,因为使用了track by $index。但是,新的0索引对象(之前的第2个)用于更新范围(为第一个对象创建的范围)。

您没有看到这反映在UI上,因为$scope.gridValues在开始时被评估过,而不会再次评估。

因此,即使$scope.appliedgrid现在指向[{span:12,data:[object]}]$scope.gridValues仍然指向[{span:6,data:[object]},{span:6,data:[grid2]}]

删除track by $index解决了这个问题,因为ng-repeat通过引用跟踪对象,因此每个对象都与相同的范围相关联,直到从数组中删除它为止。

您可以使用AngScope验证它,这是一个基于Firebug的小型范围检查器。您必须在单独的选项卡中使用&#34打开它;在单独的窗口中启动预览"为了让它在plunker中工作。

我试图找到一个快速解决方案,但没有运气。我想,你必须使用独立的范围和真正的双向绑定重新编写它。

答案 1 :(得分:1)

简短回答:从track by $index移除ng-repeat

答案很长:当你写track by $index时,你实际上是在向ng-repeat说:

  • 第一个DOM元素将与标记的对象关联为" 0"对象
  • 第二个DOM元素将与标记的对象关联为" 1"对象

从阵列中删除第一个对象时,角度摘要并找出以下内容:

  • 第一个DOM元素仍然与标记为" 0"的对象相关联。对象
  • 第二个DOM元素未与任何对象关联,因此必须将其删除

这是因为当ng-repeat再次运行时,您之前标记为" 1"的第二个对象现在是您的第一个也是唯一一个被标记为" 0&#34的对象;,因为从{0开始再次评估$index

Angular认为第一个DOM元素仍然指向同一个对象,因为它发现它被标记为" 0",不管它是一个完全不同的对象。在幕后,$scope具有正确的模型值,但ng-repeat会跳过重新呈现DOM元素。

很难写出真正发生的事情。希望我做对了,帮助过你。