如何通过ng-repeat遍历函数返回的项目?


114

我想重复创建div,这些项目是函数返回的对象。但是,以下代码报告错误:达到10个$ digest()迭代。流产!jsfiddle在这里:http//jsfiddle.net/BraveOstrich/awnqm/

<body ng-app>
  <div ng-repeat="entity in getEntities()">
    Hello {{entity.id}}!
  </div>
</body>

Answers:


195

简短的回答:您真的需要这样的功能还是可以使用属性?http://jsfiddle.net/awnqm/1/

长答案

为简单起见,我仅描述您的情况-ngRepeat对象数组。另外,我将省略一些细节。

AngularJS使用脏检查来检测更改。当应用程序启动时运行$digest$rootScope$digest将对作用域的层次结构进行深度优先遍历。所有范围都有手表清单。每个手表都有最后一个值(最初是initWatchVal)。对于所有手表的每个作用域都$digest运行它,获取当前值(watch.get(scope))并将其与进行比较watch.last。如果当前值不等于watch.last(始终用于第一次比较),则$digest设置dirtytrue。如果处理所有范围,dirty == true $digest则从开始另一个深度优先遍历$rootScope$digest当脏== false或遍历次数== 10时结束。在后一种情况下,错误“达到10 $ digest()迭代”。将被记录。

现在大约ngRepeat。对于每次watch.get调用,它将来自集合(返回值getEntities)的对象与其他信息一起存储在缓存中(HashQueueMaphashKey)。对于每个watch.get调用,ngRepeat尝试通过其hashKey缓存获取对象。如果它在缓存中不存在,则将其ngRepeat存储在缓存中,创建新的作用域,在其上放置对象,创建DOM元素,等等

现在大约hashKey。通常hashKey是由生成的唯一编号nextUid()。但这可以起作用hashKey生成后存储在对象中以供将来使用。

为什么您的示例会产生错误:函数getEntities()总是返回带有新对象的数组。该对象hashKeyngRepeat缓存中不存在也不存在。因此ngRepeat,每个watch.get手表都会为for生成新的作用域{{entity.id}}。这只手表先watch.getwatch.last == initWatchVal。这样watch.get() != watch.last。因此$digest开始新的遍历。因此ngRepeat用新的手表创造了新的范围。所以... 10个遍历后,您会得到错误。

如何解决

  1. 不要在每次getEntities()调用时都创建新对象。
  2. 如果需要创建新对象,则可以hashKey为其添加方法。有关示例,请参见本主题

希望知道AngularJS内部知识的人会在我做错事情时纠正我。


4
+1谢谢你。我有同样的问题,不能为其使用静态属性。实际上,$ hashKey应该记录在IMO手册的ngRepeat页上。
Michael Moussa 2013年

知道从1.1.3到1.1.4的变化会如何影响这一点?在1.1.4之前,这确实有效。更新日志中没有关于它的任何内容,我无法推断出区别是什么。当前的行为是有道理的。
2014年

此外,检查了这一点,如果你能:stackoverflow.com/questions/20933261/...我不知道如果我的回答是去还是不去的方式..
M59

2
因此,按照以下建议Do not create new objects on every getEntities() call.可以很容易地将其固定:<div ng-repeat="entity in entities = (entities || getEntities())">
przno 2014年

2
我以前的评论中的解决方案在万一getEntities()总是返回同一数组的情况下有效,如果该数组曾经更改,您将不会在ng-repeat
przno 2014年

44

在重复序列之外初始化数组

<body ng-app>
   <div ng-init="entities = getEntities()">
       <div ng-repeat="entity in entities">
           Hello {{entity.id}}!
       </div>
   </div>
</body>

8
如果getEntities()返回的结果与程序的生命周期不同,则此方法不起作用。举例来说,这会getEntities()触发$http.get。当get最终解析(您进行了AJAX调用)时,entities将已经初始化。
Nighto 2014年

3
从角度文档The only appropriate use of ngInit is for aliasing special properties of ngRepeat. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
Blowsie 2015年

我认为主要要点是通过任何方式“在重复序列之外初始化数组” ...和@Nighto很好的承诺
Mwayi

15

这是在这里报告的,得到了以下响应:

您的吸气剂不是幂等的,它会更改模型(通过在每次调用时生成一个新数组)。这迫使Angular继续调用它,希望模型最终能够稳定下来,但是它从没有放弃,并引发异常。

getter返回的值相等但不相同,这就是问题所在。

如果将数组移到Main控制器之外,则可以看到这种现象消失了:

var array = [{id:'angularjs'}];
function Main($scope) {
    $scope.getEntities = function(){return array;};
};

因为现在它每次都返回相同的对象。您可能需要重新设计模型,以在范围上使用属性而不是函数:

我们通过将控制器方法的结果分配给属性并对其执行ng:repeat来解决此问题。


如果函数在每次迭代中都有一个参数更改,则使用属性可能是唯一的方法。
2014年

7

基于@przno评论

<body ng-app>
  <div ng-repeat="item in t = angular.equals(t, getEntities()) ? t : getEntities()">
    Hello {{item.id}}!
  </div>
</body>

BTW第二个解决方案@Artem Andreev建议在Angular 1.1.4及更高版本中不起作用,而第一个解决方案不能解决问题。因此,现在恐怕这是不那么尖锐的解决方案,并且在功能上没有缺点


您是说item.id吗?如果entity.id是您的意思,请您解释一下?非常感谢!
Gerfried

是的,你说的没错。Item.id是应该的b。谢谢
Agat
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.