【问题标题】:How to Loop through items returned by a function with ng-repeat?如何使用 ng-repeat 循环遍历函数返回的项目?
【发布时间】:2012-09-02 10:43:03
【问题描述】:

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

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

【问题讨论】:

    标签: angularjs


    【解决方案1】:

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

    长答案

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

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

    现在关于ngRepeat。对于每个watch.get 调用,它都会将集合中的对象(返回值getEntities)与缓存中的附加信息(HashQueueMap by hashKey)一起存储。对于每个watch.get 调用ngRepeat 尝试通过其hashKey 从缓存中获取对象。如果缓存中不存在,ngRepeat 将其存储在缓存中,创建新范围,将对象放入其中,创建 DOM 元素,etc

    现在关于hashKey。通常hashKey 是由nextUid() 生成的唯一编号。但它可以是functionhashKey 生成后存储在对象中以备将来使用。

    为什么您的示例会产生错误:函数 getEntities() 总是返回带有新对象的数组。此对象没有hashKey,也不存在于ngRepeat 缓存中。因此,每个watch.get 上的ngRepeat 都会为其生成新的范围,并为{{entity.id}} 提供新的监视。这只手表在第一个watch.get 上有watch.last == initWatchVal。所以watch.get() != watch.last。所以$digest 开始新的遍历。所以ngRepeat 用新手表创建了新范围。所以......在 10 次遍历后你会得到错误。

    如何解决

    1. 不要在每次getEntities() 调用时创建新对象。
    2. 如果您需要创建新对象,您可以为它们添加hashKey 方法。有关示例,请参阅 this topic

    如果我有什么错误,希望了解 AngularJS 内部的人能纠正我。

    【讨论】:

    • +1 谢谢你。我有同样的问题,无法使用静态属性。 $$hashKey 应该真正记录在手册 IMO 的 ngRepeat 页面上。
    • 知道从 1.1.3 到 1.1.4 的变化是什么影响了这一点吗?在 1.1.4 之前,这确实有效。变更日志中没有关于它的任何内容,我无法推断出有什么区别。当前的行为是有道理的。
    • 另外,如果可以的话,请检查一下:stackoverflow.com/questions/20933261/… 我不确定我的答案是否可行..
    • 所以按照Do not create new objects on every getEntities() call.的建议可以很容易地修复它:&lt;div ng-repeat="entity in entities = (entities || getEntities())"&gt;
    • 我之前评论中的解决方案适用于getEntities() 总是返回相同的数组,如果数组发生变化,您将不会在ng-repeat 中得到它
    【解决方案2】:

    基于@przno 评论

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

    顺便说一句,@Artem Andreev 建议第二个解决方案不适用于 Angular 1.1.4 及更高版本,而第一个解决方案不能解决问题。所以,恐怕现在这是一个没有功能缺点的不那么尖锐的解决方案

    【讨论】:

    • 你的意思是item.id吗?如果 entity.id 是你的意思,你能解释一下吗?非常感谢!
    • 是的,你是对的。 Item.id 应该是 b。谢谢
    【解决方案3】:

    在重复之外初始化数组

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

    【讨论】:

    • 如果getEntities() 在程序的生命周期中返回不同的东西,这将不起作用。例如,假设getEntities() 触发了$http.get。当 get 最终解决时(您进行了 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很好地呼吁承诺
    【解决方案4】:

    这是reported here,得到了这样的回复:

    您的 getter 不是幂等的并且会更改模型(通过每次调用它时生成一个新数组)。这迫使 angular 继续调用它,希望模型最终稳定下来,但它从来没有这样做,所以 angular 放弃并抛出异常。

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

    如果将数组移到主控制器之外,您会看到此行为消失:

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

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

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

    【讨论】:

    • 如果函数在每次迭代中都有参数变化,则使用属性可能是唯一的方法。
    猜你喜欢
    • 2015-06-09
    • 1970-01-01
    • 2016-06-22
    • 2014-08-24
    • 2017-03-05
    • 1970-01-01
    • 2013-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多