Як переглядати елементи, повернуті функцією з ng-повтором?


114

Я хочу створювати divs кілька разів, елементи - це об'єкти, повернені функцією. Однак такі помилки у звіті про коди: досягнуто 10 ітерацій digest (). Аборт! jsfiddle є тут: http://jsfiddle.net/BraveOstrich/awnqm/

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

Відповіді:


195

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

Довга відповідь

Для простоти опишу лише ваш випадок - ngRepeat для масиву об’єктів. Також я опущу деякі деталі.

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

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

Тепер про hashKey. Зазвичай hashKeyце унікальне число, що генерується nextUid(). Але це може бути функція . hashKeyзберігається в об'єкті після генерації для подальшого використання.

Чому ваш приклад створює помилку : функція getEntities()завжди повертає масив з новим об'єктом. Цей об’єкт не має hashKeyі не існує в ngRepeatкеші. Таким чином, ngRepeatкожен з них watch.getгенерує нову сферу застосування для цього {{entity.id}}. Цей годинник спочатку watch.getє watch.last == initWatchVal. Отже watch.get() != watch.last. Так $digestпочинається новий траверс. Так ngRepeatстворюється новий розмах з новими годинниками. Отже ... після 10 переходів ви отримуєте помилку.

Як ви можете це виправити

  1. Не створюйте нових об'єктів під час кожного getEntities()дзвінка.
  2. Якщо вам потрібно створити нові об'єкти, ви можете додати hashKeyметод для них. Дивіться приклади в цій темі .

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


4
+1 дякую за це. У мене була та сама проблема, і я не міг використати для неї статичну властивість. $$ hashKey дійсно має бути задокументовано на ngRepeat сторінці посібника IMO.
Майкл Муса

Будь-яка ідея, що змінилося з 1.1.3 на 1.1.4, що вплинуло на це? До 1.1.4 це фактично спрацювало. У журналі змін нічого про це немає, і я не можу пояснити, у чому різниця. Поточна поведінка має сенс.
m59

Також перевірте це, чи зможете ви: stackoverflow.com/questions/20933261/… Я не впевнений, чи моя відповідь - це шлях чи ні ..
m59

2
Тож дотримуючись рекомендації до Do not create new objects on every getEntities() call.неї, можна легко виправити так:<div ng-repeat="entity in entities = (entities || getEntities())">
Прзно

2
рішення з мого попереднього коментаря працює в тому випадку, коли getEntities()завжди повертається той самий масив, якщо масив колись зміниться, ви не отримаєте його вng-repeat
przno

44

Ініціалізуйте масив поза повтором

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

8
Це не працює, якщо getEntities()повертає щось інше в життєвий цикл програми. Скажімо, наприклад, що getEntities()тригер $http.get. Коли рішення нарешті вирішиться (ви здійснили дзвінок AJAX), entitiesвже буде ініціалізовано.
Nighto

3
З кутових документів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.
Blowsie

Я думаю, що головне - "ініціалізувати масив поза повтором" будь-якими способами ... і @Nighto вдалий заклик обіцянок
Mwayi

15

Про це повідомили тут і отримали таку відповідь:

Ваш геть не є безсильним і змінює модель (генеруючи новий масив щоразу, коли він викликається). Це змушує кутовий продовжувати дзвонити, сподіваючись, що модель врешті-решт стабілізується, але вона ніколи не робить це кутовим і здає виняток.

Значення повернення геттера рівні, але не однакові, і це проблема.

Ви можете побачити, як така поведінка йде, якщо перемістити масив за межі головного контролера:

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

тому що тепер він щоразу повертає один і той же об’єкт. Можливо, вам знадобиться перепрофіктувати модель, щоб використовувати властивість в області застосування замість функції:

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


Використання властивості може бути єдиним способом, якщо функція змінює параметр на кожній ітерації.
Стефан

7

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

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

Друге рішення BTW @ Артем Андрєєв припускає, що він не працює в куті 1.1.4 і вище, тоді як перший не вирішує проблему. Тому я боюся, що це менш пікантне рішення без недоліків у функціональності


Ви маєте на увазі item.id? Якщо ви маєте на увазі entity.id, чи можете ви поясніть? Велике дякую!
Герфрід

Так, ти маєш рацію. Item.idце те, що повинно бути b. Спасибі
Агат
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.