How to Loop through items returned by a function with ng-repeat?
我想重复创建div,项是由函数返回的对象。但是,以下代码报告错误:已达到10$digest()个迭代。中止!jspiddle在这里:http://jspiddle.net/braveostrich/awnqmrich/
1 2 3 4 5
| <body ng-app>
Hello {{entity.id}}!
</body> |
简短的回答:你真的需要这样的功能还是可以使用属性?http://jsfiddle.net/awnqm/1/
长回答
为了简单起见,我将只描述您的情况—ngrepeat用于对象数组。另外,我将省略一些细节。
AngularJS使用脏检查来检测更改。当应用程序启动时,它为$rootScope运行$digest。$digest将对作用域的层次结构进行深度优先遍历。所有范围都有手表列表。每个表都有最后一个值(最初是initWatchVal)。对于所有手表的每个范围,$digest运行它,获取当前值(watch.get(scope)并将其与watch.last进行比较。如果当前值不等于watch.last(总是用于第一次比较),$digest将dirty设置为true。当处理所有作用域时,如果dirty == true$digest从$rootScope开始另一个深度首次遍历。当脏=假或遍历数=10时,$digest结束。在后一种情况下,将记录错误"10$digest()iterations reached."。
关于ngRepeat。对于每个watch.get调用,它存储来自集合的对象(返回值getEntities)以及缓存中的附加信息(HashQueueMap,由hashKey)。对于每个watch.get调用ngRepeat尝试从缓存中通过其hashKey获取对象。如果它不存在于缓存中,那么ngRepeat将它存储在缓存中,创建新的作用域,将对象放在上面,创建dom元素等。
关于hashKey。通常,hashKey是nextUid()生成的唯一数。但它可以发挥作用。生成后将hashKey存储在对象中,以备将来使用。
示例生成错误的原因:函数getEntities()总是返回带有新对象的数组。此对象没有hashKey,并且不存在于ngRepeat缓存中。因此,每个watch.get上的ngRepeat为它生成了新的范围,并为{{entity.id}}生成了新的监视。这只手表上的第一只watch.get有watch.last == initWatchVal。因此,watch.get() != watch.last。因此,$digest开始新的导线测量。因此,ngRepeat用新手表创造了新的范围。所以…经过10次遍历后,您会得到错误。
如何修复它
不要在每个getEntities()调用上创建新对象。
如果需要创建新对象,可以为它们添加hashKey方法。有关示例,请参阅本主题。
希望了解安古拉吉斯内在的人会纠正我的错误。
- +1谢谢。我有同样的问题,不能为它使用静态属性。$$hashkey应该记录在IMO手册的ngrepeat页上。
- 很好的答案,很好的细节,谢谢。"希望那些了解安古拉吉斯内里的人……呃,你想是你吧,兄弟。
- 知道是什么从1.1.3变为1.1.4影响了这一点吗?在1.1.4之前,这实际上起到了作用。在变更日志中没有任何关于它的内容,我无法解释其中的区别。当前的行为是有意义的。
- 另外,如果可以的话,也可以看看这个:stackoverflow.com/questions/20933261/&hellip;我不确定我的答案是不是可行的。
- @M59我认为这与1.1.4中添加$watchCollection()有关。$watchCollection()比较数组项(使用===与$watch()比较,后者使用equals()比较。
- 因此,根据对Do not create new objects on every getEntities() call.的建议,可以很容易地修复它:
- 我之前的评论中的解决方案在getEntities()总是返回相同的数组时有效,如果数组发生变化,您将无法在ng-repeat中得到它。
在重复之外初始化数组
1 2 3 4 5 6 7
| <body ng-app>
Hello {{entity.id}}!
</body> |
- 如果getEntities()在程序的生命周期中返回不同的东西,这就不起作用。例如,假设getEntities()触发$http.get。当get finally resolve(您进行了Ajax调用)时,entities将已经初始化。
- 来自角文档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.。
- 我认为重点是"在重复之外初始化数组",无论用什么方法…和@nighto good call on promises
这里报告了这一点,并得到了以下回复:
Your getter is not idempotent and changes the model (by generating a new array each time it is called). This is forcing angular to keep on calling it in hope that the model will eventually stabilize, but it never does so angular gives up and throws an exception.
The values the getter return are equal but not identical and that's the problem.
< /块引用>
如果将数组移到主控制器之外,可以看到此行为消失:
1 2 3 4
| var array = [{id:'angularjs'}];
function Main($scope) {
$scope.getEntities = function(){return array;};
}; |
因为现在它每次返回相同的对象。您可能需要重新设计您的模型,以便在作用域上使用属性而不是函数:
We worked around it by assigning the result of the controller's method to a property, and doing ng:repeat against it.
< /块引用>
- 如果函数在每次迭代中都有一个参数发生更改,那么使用属性可能是唯一的方法。
基于@przno评论
1 2 3 4 5
| <body ng-app>
Hello {{item.id}}!
</body> |
btw第二个解决方案@artem andreev建议在角度为1.1.4或更大的情况下不工作,而第一个解决方案无法解决问题。所以,现在恐怕这是一个不那么尖锐的解决方案,功能上没有缺点
- 你是说item.id吗?如果entity.id是你的意思,你能解释一下吗?多谢!
- 是的,你说得对。Item.id应该是b.谢谢。
- 聪明的解决方案!