【问题标题】:Why does `ng-repeat` on filtered dictionary cause `10 $digest() iterations reached` error?为什么过滤字典上的“ng-repeat”会导致“10 $digest() 迭代次数达到”错误?
【发布时间】:2025-07-19 22:50:02
【问题描述】:

考虑以下plunker

这里是html

    <div ng-repeat="(id, testOject) in filterList()">
      <div ng-if="testOject['state']">
        {{testObject}}
      </div>
    </div>

这里是相关的js

  $scope.test = {
    '1': {'state': true, 'label': '1'}
  }

  $scope.filterList = function() {
    var map = {};

    for (var key in $scope.test){
      if($scope.test[key]['state']) {
        map[key] = {
          'state': $scope.test[key]['state'],
          'label': $scope.test[key]['label']
        }
      }
    }
    return map;
  };

以上代码导致10 $digest() iterations reached错误。

但是,如果我们稍微修改一下code

  $scope.filterList = function() {
    var map = {};

    for (var key in $scope.test){
      if($scope.test[key]['state']) {
        map[key] = true
      }
    }
    return map;
  };

错误没有发生

似乎触发了更改检测,当更新了包含全新对象的新key,但我的字典中只有 1 个对象时,为什么它会循环通过更改检测 10 次?

【问题讨论】:

  • 将执行摘要,同时您的范围发生变化。如果数据已经过滤,而不是新创建的,过滤器应该返回相同的对象/数组。

标签: javascript angularjs dictionary


【解决方案1】:

From error page:

当应用程序的模型变得不稳定并且 每个$digest 循环都会触发状态更改和后续$digest 循环。 Angular 检测到这种情况并防止无限循环 以免导致浏览器无响应。

例如,可以通过在路径上设置监视来发生这种情况 并随后在值更改时更新相同的路径。

 $scope.$watch('foo', function() {
      $scope.foo = $scope.foo + 1;
    });

一个常见的错误是绑定到一个生成新的函数 每次调用数组。例如:

<div ng-repeat="user in getUsers()">{{ user.name }}</div>

...

$scope.getUsers = function() {
  return [ { name: 'Hank' }, { name: 'Francisco' } ];
};

由于getUsers() 返回一个新数组,Angular 确定 每个$digest 循环的模型都不同,从而导致错误。这 解决方案是如果元素没有返回相同的数组对象 改变:

var users = [ { name: 'Hank' }, { name: 'Francisco' } ];

$scope.getUsers = function() {
  return users;
};

$digest 循环允许的最大迭代次数为 通过 TTL 设置控制,可以通过配置 $rootScopeProvider.

对于您的情况,您可以使用以下代码:

Working Demo

  var map = {};
  $scope.createArray = function() {       

    for (var key in $scope.test){
      if($scope.test[key]['state']) {
        map[key] = {
          'state': $scope.test[key]['state'],
          'label': $scope.test[key]['label']
        }
      }
    }
    return map; 
  };

  $scope.filterList = function() {
      return map; 
  }

【讨论】:

    【解决方案2】:

    来自docs

    The $digest loop keeps iterating until the model stabilizes
    

    即当您的模型在 2 个连续周期之间不再发生变化时,摘要将停止。

    代码:

        map[key] = {
          'state': $scope.test[key]['state'],
          'label': $scope.test[key]['label']
        }
    

    在每个摘要循环中为 map[key] 分配一个新对象,因此模型永远不会稳定。

    代码:

    map[key] = true
    

    将原始布尔值分配给map[key],该值在 2 个连续的摘要周期之间不会发生变化,因此模型变得稳定。

    为了稳定您的地图,您可以创建一个范围变量并更改map[key] 对象的属性。见plunker

      $scope.map = {};
    
      $scope.filterList = function() {
        for (var key in $scope.test){
          if($scope.test[key]['state']) {
            $scope.map[key] = $scope.map[key] || {};
            $scope.map[key].state = $scope.test[key]['state'];
            $scope.map[key].label = $scope.test[key]['label'];
          }
        }
    
        return $scope.map;
      };
    

    【讨论】:

    • 但它只改变一次,因为我的字典是一号的,所以它应该在 10 个循环的限制内?
    • @testing 我不是指for (var key in $scope.test)。我指的是摘要循环。所以循环 1,filterList() 被调用,$scope.map['1'] = newObject。循环 2,调用 filterList() 并 $scope.map['1'] = anotherNewObject。所以$scope.map['1'] 是摘要周期 1 和 2 之间的不同对象,因此周期 3 开始。周期 3 将不同于周期 2,因此周期 4 将开始,依此类推...
    最近更新 更多