错误:达到$ $ digest()迭代。中止!使用动态排序谓词

时间:2013-01-17 10:23:45

标签: angularjs javascript-framework

我有以下代码重复并显示用户名和他的分数:

<div ng-controller="AngularCtrl" ng-app>
  <div ng-repeat="user in users | orderBy:predicate:reverse | limitTo:10">
    <div ng-init="user.score=user.id+1">
        {{user.name}} and {{user.score}}
    </div>
  </div>
</div>

和相应的角度控制器。

function AngularCtrl($scope) {
    $scope.predicate = 'score';
    $scope.reverse = true;
    $scope.users = [{id: 1, name: 'John'}, {id: 2, name: 'Ken'}, {id: 3, name: 'smith'}, {id: 4, name: 'kevin'}, {id: 5, name: 'bob'}, {id: 6, name: 'Dev'}, {id: 7, name: 'Joe'}, {id: 8, name: 'kevin'}, {id: 9, name: 'John'}, {id: 10, name: 'Ken'}, {id: 11, name: 'John'}, {id: 1, name: 'John'}, {id: 2, name: 'Ken'}, {id: 3, name: 'smith'}, {id: 4, name: 'kevin'}, {id: 5, name: 'bob'}, {id: 6, name: 'Dev'}, {id: 7, name: 'Joe'}, {id: 8, name: 'kevin'}, {id: 9, name: 'John'}, {id: 10, name: 'Ken'}]
}

当我运行上面的代码时,我得到了错误:10 $ digest()迭代。在我的控制台中中止!错误。

我为此创建了jsfiddle

仅在ng-repeat内部初始化排序谓词,并且还对对象数量应用限制。所以我觉得将sortby和limitTo观察者放在一起就是错误的原因。

如果$ scope.reverse为false(得分的升序),那么它不会出错。

有谁能帮我理解这里有什么问题?非常感谢你的帮助。

13 个答案:

答案 0 :(得分:112)

请检查jsFiddle。 (代码与您发布的代码基本相同,但我使用元素而不是窗口来绑定滚动事件。)

据我所知,您发布的代码没有问题。当您在属性上创建更改循环时,通常会出现您提到的错误。例如,当您观察某个属性的更改,然后在侦听器上更改该属性的值时:

$scope.$watch('users', function(value) {
  $scope.users = [];
});

这将导致错误消息:

  

未捕获错误:达到10 $ digest()次迭代。中止!
  看守   在最近5次迭代中被解雇:...

确保您的代码没有这种情况。

<强>更新

这是你的问题:

<div ng-init="user.score=user.id+1"> 

你不应该在渲染过程中更改对象/模型,否则它将强制执行新的渲染(并因此导致循环,这会导致'错误:10 $ digest()迭代到了。中止!')。

如果要更新模型,请在Controller或指令上执行,而不要在视图上执行。 angularjs文档recommends not to use ng-init完全是为了避免这种情况:

  

在模板中使用ngInit指令(仅限玩具/示例应用程序,而不是   推荐用于实际应用)

这是一个带有工作示例的jsFiddle

答案 1 :(得分:8)

对我来说这个错误的原因是......

ng-if="{{myTrustSrc(chat.src)}}"

在我的模板中

它导致我的控制器中的函数myTrustSrc在无限循环中被调用。如果从此行中删除ng-if,则问题就解决了。

<iframe ng-if="chat.src" id='chat' name='chat' class='chat' ng-src="{{myTrustSrc(chat.src)}}"></iframe>

当不使用ng-if时,该函数仅被调用几次。我仍然想知道为什么用ng-src多次调用该函数?

这是控制器中的功能

$scope.myTrustSrc = function(src) {
    return $sce.trustAsResourceUrl(src);
}

答案 2 :(得分:5)

对我而言,我正在传递一个函数结果作为双向绑定输入&#39; =&#39;每次都在创建一个新对象的指令。

所以我有类似的东西:

<my-dir>
   <div ng-repeat="entity in entities">
      <some-other-dir entity="myDirCtrl.convertToSomeOtherObject(entity)"></some-other-dir>
   </div>
</my-dir>

和my-dir上的控制器方法是

this.convertToSomeOtherObject(entity) {
   var obj = new Object();
   obj.id = entity.Id;
   obj.value = entity.Value;
   [..]
   return obj;
}

当我做到

this.convertToSomeOtherObject(entity) {
   var converted = entity;
   converted.id = entity.Id;
   converted.value = entity.Value;
   [...]
   return converted;
}

解决了这个问题!

希望这会对某人有所帮助:)。

答案 3 :(得分:4)

我有另一个导致这种情况的例子。希望它有助于将来参考。我使用的是AngularJS 1.4.1。

我有这个标记,多次调用自定义指令:

<div ng-controller="SomeController">
    <myDirective data="myData.Where('IsOpen',true)"></myDirective>
    <myDirective data="myData.Where('IsOpen',false)"></myDirective>
</div>

myData是一个数组,Where()是一个扩展方法,它遍历数组,返回一个新数组,其中包含IsOpen属性与第二个参数中的bool值匹配的原始项目。 / p>

在控制器中,我设置$scope.data,如下所示:

DataService.getData().then(function(results){
    $scope.data = results;
});

从上面的标记中调用Where()扩展方法就是问题所在。为解决此问题,我将调用扩展方法转移到控制器而不是标记:

<div ng-controller="SomeController">
    <myDirective data="openData"></myDirective>
    <myDirective data="closedData"></myDirective>
</div>

和新的控制器代码:

DataService.getData().then(function(results){
    $scope.openData = results.Where('IsOpen',true);
    $scope.closedData = results.Where('IsOpen',false);
});

答案 4 :(得分:2)

对于初学者而言,请忽略所有答案并告诉您使用$ watch。 Angular已经从听众那里起作用了。我保证你只是在朝着这个方向思考,使你复杂化。

忽略告诉您用户$ timeout的所有答案。您无法知道页面加载需要多长时间,因此这不是最佳解决方案。

您只需知道页面何时完成呈现。

<div ng-app='myApp'>
<div ng-controller="testctrl">
    <label>{{total}}</label>
    <table>
      <tr ng-repeat="item in items track by $index;" ng-init="end($index);">
        <td>{{item.number}}</td>
      </tr>
    </table>
</div>

var app = angular.module('myApp', ["testctrl"]);
var controllers = angular.module("testctrl", []);
 controllers.controller("testctrl", function($scope) {

  $scope.items = [{"number":"one"},{"number":"two"},{"number":"three"}];

  $scope.end = function(index){
  if(index == $scope.items.length -1
        && typeof $scope.endThis == 'undefined'){

            ///  DO STUFF HERE
      $scope.total = index + 1;
      $scop.endThis  = true;
 }
}
});

通过$ index跟踪ng-repeat,当数组的长度等于索引时,停止循环并执行逻辑。

jsfiddle

答案 5 :(得分:2)

派对相当晚,但我的问题正在发生,因为角度为1.5.8的ui-router存在缺陷。值得一提的是,这个错误只出现在我第一次运行应用程序时,之后就不会再出现了。 来自github的This post解决了我的问题。 基本上错误涉及$urlRouterProvider.otherwise("/home") 解决方案是这样的解决方法:

$urlRouterProvider.otherwise(function($injector, $location) {
   var $state = $injector.get("$state");
   $state.go("your-state-for-home");
});

答案 6 :(得分:1)

很奇怪......我从同样的事情中得到了完全相同的错误。当我创建我的控制器时,我传递了$ location参数,如下所示:

App.controller('MessageController', function ($scope, $http, $log, $location, $attrs, MessageFactory, SocialMessageFactory) {
   // controller code
});

这被证明是bug 当我们使用第三方库或纯JS来操作某些细节(这里是window.location)时,角度的下一个摘要将会导致此错误。

所以我只是从控制器创建参数中删除了 $ location ,它再次起作用,没有出现此错误。 或者如果你绝对需要使用角度的$ location,你必须删除模板页面链接中的每一个<a href="#">link</a>,而是写 href =&#34;&#34; 。为我工作。

希望有一天能有所帮助。

答案 7 :(得分:1)

我在角树控制的上下文中遇到了这个错误。 在我的例子中,它是树选项。我从函数返回treeOptions()。它总是返回相同的对象。但是Angular神奇地认为它是一个新的对象然后导致消化周期开始。导致摘要的递归。 解决方案是将treeOptions绑定到范围。并且只分配一次。

答案 8 :(得分:0)

如果使用angular,请从浏览器控制台中删除ng-storage配置文件。 它不是一般的解决方案,它在我的案例中起作用。

In Chrome F12->Resources->Local Storage

答案 9 :(得分:0)

在将Firefox升级到版本51之后,发生了这种情况。在clearing the cache之后,问题已经消失。

答案 10 :(得分:0)

我有类似的错误,因为我定义了

  

纳克级=&#34; GetLink()&#34;

而不是

  

纳克单击=&#34; GetLink()&#34;

答案 11 :(得分:0)

在使用$ sce.trustAsResourceUrl()作为从ng-src调用的函数的返回值时,从角度1.6-> 1.7升级后,这发生在我身上。您可以看到here中提到的问题。

就我而言,我必须更改以下内容。

<source ng-src="{{trustSrc(someUrl)}}" type='video/mp4' />
trustSrc = function(url){
    return $sce.trustAsResourceUrl(url);
};

<source ng-src='{{trustedUrl}} type='video/mp4' />
trustedUrl = $sce.trustAsResourceUrl(someUrl);

答案 12 :(得分:0)

当我在自定义排序过滤器中调用Array.reverse()

时,使用AngularJS 1.3.9遇到了完全相同的错误

删除后,一切都很好。