Ошибка AngularJS InfDig (бесконечный цикл) с функцией ng-repeat, которая возвращает массив объектов

Здесь мой код:

<h1 ng-repeat="item in func()">something</h1>

$scope.func = function(){
  return [{"property" : "value1"},{"property": "value2"}];
}

В Angular.js v. 1.1.1 нет ошибки. В Angular.JS v 1.2.1 я получаю ошибку infDig.

Скриншот v.1.1.1

Скриншот v.1.2.1

Не могли бы вы объяснить эту ситуацию? Большое спасибо.

Ответ 1

По сравнению с AngularJS 1.2: Выражение "track by" было добавлено в ng-repeat и более правильно решает эту проблему, как показано в следующем коде.

<h1 ng-repeat="item in func() track by $index">something</h1>

$scope.func = function(){
  return [{"property" : "value1"},{"property": "value2"}];
}

Следующая статья помогает понять выражение более подробно и почему это так полезно, особенно при работе с $$ haskey Использование Track-By с ngRepeat In AngularJS 1.2 от Ben Nadal.

Проблема в том, что вы каждый раз создаете новый массив, поэтому нужно что-то новое, чтобы angular отслеживать. Насколько я могу судить, ng-repeat запускается, а затем снова проверяет свою коллекцию, чтобы увидеть, что-либо изменилось в этом цикле. Поскольку функция возвращает новый массив, который воспринимается как изменение.

Проверьте это: http://jsfiddle.net/kL5YZ/. Если вы посмотрите в console.log и нажмите кнопку, вы увидите, что свойство $$hashKey объектов изменяется каждый раз, когда выполняется ng-repeat.

Изменение происходит начиная с версии 1.1.4, но журнал изменений не дает никаких указаний относительно того, почему поведение отличается. Новое поведение имеет для меня больше смысла.

Вот отличный пост, который я нашел, объясняя текущее поведение в глубину: Как петля через элементы, возвращаемые функцией с помощью ng-repeat?

Если вы каждый раз возвращаете один и тот же объект/массив, у вас не будет ошибки. Вы можете иметь кеш функции, который он создает на основе аргументов, и всегда возвращать тот же массив/объект, когда эти аргументы передаются. Таким образом, myFunc ('foo') всегда будет возвращать тот же массив, а не новый, который выглядит одна и та же. См. Примечания в моем коде ниже. Живая демонстрация (нажмите).

<div ng-repeat="foo in foos">
  <div ng-repeat="bar in barFunc(foo)">{{bar.text}}</div>
  <div ng-repeat="bar in barFunc('test')">{{bar.text}}</div>
</div>

JavaScript:

var app = angular.module('myApp', []);

app.controller('myCtrl', function($scope, myService) {
  $scope.foos = [
    'a','b','c'
  ];
  //I put this into a service to avoid cluttering the controller
  $scope.barFunc = myService.getObj;
});

app.factory('myService', function() {
  /*
   * anything created will be stored in this cache object,
   * based on the arguments passed to `getObj`.
   * If you need multiple arguments, you could form them into a string,
   * and use that as the cache key
   * since there only one argument here, I'll just use that
   */
  var cache = {};

  var myService = {
    getObj : function(val) {
      //if we haven't created an array with this argument before
      if (!cache[val]) {
        //create one and store it in the cache with that argument as the key
        cache[val] = [
          {text:val}
        ];
      }
      //return the cached array
      return cache[val];
    }
  };

  return myService;
});