Как перебирать элементы, возвращаемые функцией с помощью ng-repeat?

Я хочу создавать divs повторно, элементы - это объекты, возвращаемые функцией. Однако следующие ошибки отчета о коде: Достигнуто 10 $digest() итераций. Aborting! jsfiddle здесь: http://jsfiddle.net/BraveOstrich/awnqm/

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

Ответы

Ответ 1

Короткий ответ: вам действительно нужна такая функция, или вы можете использовать свойство? http://jsfiddle.net/awnqm/1/

Длинный ответ

Для простоты я опишу только ваш случай - ngRepet для массива объектов. Кроме того, я опускаю некоторые подробности.

Функция AngularJS использует грязную проверку для обнаружения изменений. Когда приложение запускается, он запускает $digest для $rootScope. $digest выполнит обход глубины для иерархии областей. У всех областей есть список часов. Каждый вахта имеет последнее значение (первоначально initWatchVal). Для каждой области для всех часов $digest запускает ее, получает текущее значение (watch.get(scope)) и сравнивает его с watch.last. Если текущее значение не равно watch.last (всегда для первого сравнения) $digest установите dirty в true. Когда все области обработки обрабатываются, если dirty == true $digest запускает другой обход глубины из $rootScope. $digest заканчивается, когда dirty == false или количество обходов == 10. В последнем случае происходит ошибка "10 $digest()". будет зарегистрирован.

Теперь о ngRepeat. Для каждого вызова watch.get он хранит объекты из коллекции (возвращаемое значение getEntities) с дополнительной информацией в кеше (HashQueueMap hashKey). Для каждого вызова watch.get ngRepeat попытайтесь получить объект из hashKey из кеша. Если он не существует в кеше, ngRepeat хранит его в кеше, создает новую область, помещает на нее объект, создает элемент DOM, и т.д..

Теперь о hashKey. Обычно hashKey - это уникальный номер, созданный nextUid(). Но это может быть функция. hashKey хранится в объекте после генерации для будущего использования.

Почему ваш пример генерирует ошибку: function getEntities() всегда возвращает массив с новым объектом. Этот объект не имеет hashKey и не существует в кеше ngRepeat. Итак, ngRepeat на каждом watch.get создает для него новую область с новыми часами для {{entity.id}}. Эти часы на первом watch.get имеют watch.last == initWatchVal. Итак, watch.get() != watch.last. Итак, $digest начинает новый траверс. Таким образом, ngRepeat создает новую область с новыми часами. Итак... после 10 траверсов вы получаете ошибку.

Как вы можете исправить это

  • Не создавайте новые объекты при каждом вызове getEntities().
  • Если вам нужно создать новые объекты, вы можете добавить для них метод hashKey. Для примера см. этот раздел.

Надеюсь, что люди, знакомые с внутренними органами AngularJS, исправят меня, если я ошибаюсь.

Ответ 2

Инициализировать массив вне повторения

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

Ответ 3

Это было сообщено здесь и получило этот ответ:

Ваш получатель не является идемпотентным и изменяет модель (путем генерации нового массива каждый раз, когда он вызывается). Это вынуждает angular продолжать называть его в надежде, что модель в конечном итоге стабилизируется, но она никогда не делает этого angular дает и исключает исключение.

     

Значения return getter равны, но не идентичны и что проблема.

Вы можете видеть, что это поведение исчезает, если вы перемещаете массив вне главного контроллера:

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

потому что теперь он возвращает один и тот же объект каждый раз. Возможно, вам придется перепроектировать вашу модель для использования свойства в области видимости вместо функции:

Мы работали над этим, назначив результат метода контроллера свойству и выполнив ng: повторим его.

Ответ 4

На основе комментария @przno

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

Второе решение BTW @Артем Андреев предлагает не работать в Angular 1.1.4 и выше, в то время как первый не решает проблему. Итак, я боюсь, что это менее острое решение без недостатков в функциональности