117 votes

Comment parcourir en boucle les éléments renvoyés par une fonction avec ng-repeat ?

Je veux créer des divs de manière répétée, les éléments sont des objets retournés par une fonction. Cependant le code suivant rapporte des erreurs : 10 itérations $digest() atteintes. Abandon ! jsfiddle est ici : http://jsfiddle.net/BraveOstrich/awnqm/

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

200voto

Artem Andreev Points 9057

Réponse courte : avez-vous vraiment besoin d'une telle fonction ou pouvez-vous utiliser une propriété ? http://jsfiddle.net/awnqm/1/

Réponse longue

Pour simplifier, je ne décrirai que votre cas - ngRepeat pour un tableau d'objets. Aussi, j'omettrai certains détails.

AngularJS utilise vérification de la saleté pour détecter les changements. Lorsque l'application est lancée, elle exécute $digest pour $rootScope . $digest fera une traversée en profondeur pour hiérarchie du champ d'application . Tous les scopes ont une liste de montres. Chaque montre a une dernière valeur (initialement initWatchVal ). Pour chaque portée, pour toutes les montres $digest l'exécute, obtient la valeur actuelle ( watch.get(scope) ) et le compare à watch.last . Si la valeur actuelle n'est pas égale à watch.last (toujours pour la première comparaison) $digest fixe dirty à true . Lorsque tous les scopes sont traités si dirty == true $digest commence une autre traversée en profondeur à partir de $rootScope . $digest se termine lorsque dirty == false ou le nombre de traversées == 10. Dans ce dernier cas, l'erreur "10 itérations $digest() atteintes." sera enregistrée.

Maintenant à propos ngRepeat . Pour chaque watch.get l'appel stocke les objets de la collection (en retournant la valeur de l'option getEntities ) avec des informations supplémentaires dans le cache ( HashQueueMap par hashKey ). Pour chaque watch.get appelez ngRepeat essaie d'obtenir l'objet par son hashKey de la cachette. S'il n'existe pas dans le cache, ngRepeat le stocke dans le cache, crée une nouvelle portée, y place un objet, crée un élément DOM, etc. .

Maintenant à propos hashKey . Habituellement hashKey est un numéro unique généré par nextUid() . Mais cela peut être fonction . hashKey est stocké dans l'objet après sa génération pour une utilisation future.

Pourquoi votre exemple génère une erreur : fonction getEntities() retourne toujours un tableau avec un nouvel objet. Cet objet n'a pas hashKey et n'existe pas dans ngRepeat cache. Ainsi, ngRepeat sur chaque watch.get génère une nouvelle portée pour elle avec une nouvelle veille pour {{entity.id}} . Cette montre au premier watch.get a watch.last == initWatchVal . Donc watch.get() != watch.last . Donc $digest commence une nouvelle traversée. Donc ngRepeat crée une nouvelle portée avec une nouvelle montre. Donc ... après 10 traversées, vous obtenez une erreur.

Comment le réparer

  1. Ne créez pas de nouveaux objets à chaque getEntities() appeler.
  2. Si vous devez créer de nouveaux objets, vous pouvez ajouter hashKey méthode pour eux. Voir ce sujet par exemple.

J'espère que les personnes qui connaissent les rouages d'AngularJS me corrigeront si je me trompe.

4 votes

+1 merci pour cela. J'avais le même problème et je ne pouvais pas utiliser une propriété statique pour cela. $$hashKey devrait vraiment être documenté sur la page ngRepeat du manuel IMO.

0 votes

Avez-vous une idée de ce qui a changé entre la version 1.1.3 et la version 1.1.4 et qui a affecté ce fonctionnement ? Avant la version 1.1.4, cela fonctionnait. Il n'y a rien dans le changelog à ce sujet et je n'arrive pas à comprendre quelle est la différence. Le comportement actuel est logique.

0 votes

Aussi, regardez ça si vous pouvez : stackoverflow.com/questions/20933261/ Je ne suis pas sûr que ma réponse soit la bonne ou non

45voto

Mwayi Points 443

Initialiser le tableau en dehors de la répétition

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

9 votes

Cela ne fonctionne pas si le getEntities() renvoie quelque chose de différent dans le cycle de vie du programme. Disons, par exemple, que getEntities() déclencher une $http.get . Lorsque l'obtention est finalement résolue (vous avez fait l'appel AJAX), entities sera déjà initialisé.

3 votes

Extrait de la documentation d'Angular 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.

0 votes

Je pense que le point principal est "d'initialiser le tableau en dehors de la répétition" par n'importe quel moyen ... et @Nighto bon appel sur les promesses

16voto

Dennis Points 19148

C'était rapporté ici et j'ai reçu cette réponse :

Votre getter n'est pas idempotent et modifie le modèle (en générant un nouveau tableau à chaque fois qu'il est appelé). Cela oblige angular à continuer à l'appeler dans l'espoir que le modèle finira par se stabiliser, mais cela n'arrive jamais, alors angular abandonne et lève une exception.

Les valeurs que les getter renvoient sont égales mais pas identiques et c'est là le problème.

Vous pouvez constater que ce comportement disparaît si vous déplacez le tableau en dehors du contrôleur principal :

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

parce que maintenant il renvoie le même objet à chaque fois. Vous devrez peut-être ré-architecturer votre modèle pour utiliser une propriété sur la portée au lieu d'une fonction :

Nous l'avons contourné en assignant le résultat de la méthode du contrôleur à une propriété, et en faisant ng:repeat sur celle-ci.

0 votes

L'utilisation d'une propriété peut être le seul moyen si la fonction a un paramètre qui change à chaque itération.

8voto

Agat Points 142

Basé sur le commentaire de @przno

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

La deuxième solution proposée par @Artem Andreev ne fonctionne pas dans Angular 1.1.4 et plus, tandis que la première ne résout pas le problème. Donc, je crains que pour l'instant, c'est la solution la moins piquante sans inconvénients en termes de fonctionnalité.

0 votes

Voulez-vous dire item.id ? Si vous voulez dire entity.id, pouvez-vous m'expliquer ? Merci beaucoup !

0 votes

Oui, vous avez raison. Item.id est ce qu'il devrait être. Merci

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X