141 votes

Erreur : 10 itérations $digest() atteintes. Abandon ! avec le prédicat sortby dynamique

J'ai le code suivant qui répète et affiche le nom de l'utilisateur et son score :

<div ng-controller="AngularCtrl" ng-app>
  <div ng-repeat="user in users | orderBy:predicate:reverse | limitTo:10">
    <div ng-init="user.score=user.id+1">
        {{user.name}} and {{user.score}}
    </div>
  </div>
</div>

Et le contrôleur angulaire correspondant.

function AngularCtrl($scope) {
    $scope.predicate = 'score';
    $scope.reverse = true;
    $scope.users = [{id: 1, name: 'John'}, {id: 2, name: 'Ken'}, {id: 3, name: 'smith'}, {id: 4, name: 'kevin'}, {id: 5, name: 'bob'}, {id: 6, name: 'Dev'}, {id: 7, name: 'Joe'}, {id: 8, name: 'kevin'}, {id: 9, name: 'John'}, {id: 10, name: 'Ken'}, {id: 11, name: 'John'}, {id: 1, name: 'John'}, {id: 2, name: 'Ken'}, {id: 3, name: 'smith'}, {id: 4, name: 'kevin'}, {id: 5, name: 'bob'}, {id: 6, name: 'Dev'}, {id: 7, name: 'Joe'}, {id: 8, name: 'kevin'}, {id: 9, name: 'John'}, {id: 10, name: 'Ken'}]
}

Lorsque j'exécute le code ci-dessus, j'obtiens le message suivant Erreur : 10 itérations de $digest() atteintes. Abandon ! erreur dans ma console.

J'ai créé jsfiddle pour le même.

Le prédicat de tri n'est initialisé qu'à l'intérieur du ng-repeat et la limite est également appliquée au nombre d'objets. Je pense donc que le fait d'avoir les deux surveillants sortby et limitTo ensemble est la raison de l'erreur.

Si la valeur de $scope.reverse est fausse (ordre ascendant du score), alors il n'y a pas d'erreur.

Quelqu'un peut-il m'aider à comprendre ce qui ne va pas ici ? J'apprécie beaucoup votre aide.

0 votes

Si vous supprimez l'instruction if, l'erreur persiste-t-elle ?

0 votes

Merci pour votre réponse Mathew ! J'ai mal diagnostiqué le problème. Le problème semble concerner les filtres sortby et limitTo. J'ai mis à jour la question avec JSFiddle. J'apprécie beaucoup votre aide.

0 votes

C'est un truc angulaire. Vous devez mémoriser vos fonctions et faire en sorte qu'elles se souviennent de l'état. Regardez ma réponse sur stackoverflow.com/questions/14376879/

117voto

bmleite Points 11359

Veuillez vérifier ceci jsFiddle . (Le code est fondamentalement le même que celui que vous avez posté, mais j'utilise un élément au lieu de la fenêtre pour lier les événements de défilement).

D'après ce que je peux voir, il n'y a aucun problème avec le code que vous avez posté. L'erreur que vous mentionnez se produit normalement lorsque vous créez une boucle de changements sur une propriété. Par exemple, lorsque vous surveillez les modifications d'une certaine propriété et que vous modifiez ensuite la valeur de cette propriété sur l'écouteur :

$scope.$watch('users', function(value) {
  $scope.users = [];
});

Il en résultera un message d'erreur :

Erreur involontaire : 10 itérations de $digest() atteintes. Abandon !
Observateurs tirés dans les 5 dernières itérations : ...

Assurez-vous que votre code ne présente pas ce genre de situations.

mettre à jour :

C'est votre problème :

<div ng-init="user.score=user.id+1"> 

Vous ne devez pas modifier les objets/modèles pendant le rendu, sinon, cela forcera un nouveau rendu (et par conséquent une boucle ce qui entraîne le Erreur : 10 itérations de $digest() atteintes. Abandon ! ).

Si vous voulez mettre à jour le modèle, faites-le sur le contrôleur ou sur une directive, jamais sur la vue. Documentation angularjs recommande de ne pas utiliser el ng-init exactement pour éviter ce genre de situations :

Utiliser la directive ngInit dans les modèles (pour les applications d'exemple uniquement, non recommandé pour les applications réelles) recommandé pour les applications réelles)

Voici un jsFiddle avec un exemple concret.

1 votes

Pour info, au lieu de angular.element(w).bind() vous pouvez utiliser element.bind() .

0 votes

Merci beaucoup à bmleite et Mark. J'ai diagnostiqué le problème de façon erronée. Veuillez voir mes commentaires ci-dessus. J'ai mis à jour la question avec JSFiddle. J'apprécie beaucoup votre aide.

0 votes

Merci beaucoup @bmleite ! J'essaie de mieux comprendre. Mais pourquoi si $scope.reverse=false (ordre ascendant du score) ne provoque pas d'erreur ?

9voto

nathan Points 86

La cause de cette erreur pour moi était...

ng-if="{{myTrustSrc(chat.src)}}"

dans mon modèle

La fonction myTrustSrc de mon contrôleur est alors appelée dans une boucle sans fin. Si je supprime le ng-if de cette ligne, le problème est résolu.

<iframe ng-if="chat.src" id='chat' name='chat' class='chat' ng-src="{{myTrustSrc(chat.src)}}"></iframe>

La fonction n'est appelée que quelques fois lorsque ng-if n'est pas utilisé. Je me demande toujours pourquoi la fonction est appelée plus d'une fois avec ng-src ?

Voici la fonction dans le contrôleur

$scope.myTrustSrc = function(src) {
    return $sce.trustAsResourceUrl(src);
}

0 votes

Je me demande toujours pourquoi la fonction est appelée plus d'une fois avec ng-src ? - parce que angular essaie de créer le html plusieurs fois jusqu'à ce qu'il obtienne 2 résultats identiques. c'est la signification de l'erreur 10 $digest() itérations atteintes, il a essayé 10 fois et n'a jamais obtenu 2 résultats identiques

0 votes

@bresleveloper pouvez-vous fournir un lien avec cette information ? Merci.

0 votes

@Willa regarde ici docs.angularjs.org/api/ng/type/$rootScope.Scope CTRF+F ce "La limite d'itération de réexécution est de 10 pour éviter un blocage de boucle infinie".

7voto

Pour moi, c'était parce que je passais le résultat d'une fonction en tant qu'entrée de liaison bidirectionnelle '=' à une directive qui créait un nouvel objet à chaque fois.

donc j'avais quelque chose comme ça :

<my-dir>
   <div ng-repeat="entity in entities">
      <some-other-dir entity="myDirCtrl.convertToSomeOtherObject(entity)"></some-other-dir>
   </div>
</my-dir>

et la méthode de contrôle sur mon-dir était

this.convertToSomeOtherObject(entity) {
   var obj = new Object();
   obj.id = entity.Id;
   obj.value = entity.Value;
   [..]
   return obj;
}

qui, lorsque j'ai fait

this.convertToSomeOtherObject(entity) {
   var converted = entity;
   converted.id = entity.Id;
   converted.value = entity.Value;
   [...]
   return converted;
}

a résolu le problème !

J'espère que cela aidera quelqu'un :)

5voto

squillman Points 4837

J'ai un autre exemple de quelque chose qui a causé cela. Espérons que cela aidera pour une référence future. J'utilise AngularJS 1.4.1.

J'avais ce balisage avec de multiples appels à une directive personnalisée :

<div ng-controller="SomeController">
    <myDirective data="myData.Where('IsOpen',true)"></myDirective>
    <myDirective data="myData.Where('IsOpen',false)"></myDirective>
</div>

myData est un tableau et Where() est une méthode d'extension qui itère sur le tableau en retournant un nouveau tableau contenant tous les éléments de l'original dont la propriété IsOpen correspond à la valeur bool du second paramètre.

Dans le contrôleur, j'ai mis $scope.data comme ça :

DataService.getData().then(function(results){
    $scope.data = results;
});

Appeler le Where() de la directive, comme dans le balisage ci-dessus, était le problème. Pour résoudre ce problème, j'ai déplacé l'appel à la méthode d'extension dans le contrôleur au lieu de la balise :

<div ng-controller="SomeController">
    <myDirective data="openData"></myDirective>
    <myDirective data="closedData"></myDirective>
</div>

et le nouveau code du contrôleur :

DataService.getData().then(function(results){
    $scope.openData = results.Where('IsOpen',true);
    $scope.closedData = results.Where('IsOpen',false);
});

2voto

Jed Lynch Points 849

Pour commencer, ignorez toutes les réponses qui vous disent d'utiliser $watch. Angular fonctionne déjà à partir d'un listener. Je vous garantis que vous compliquez les choses en pensant simplement dans cette direction.

Ignorez toutes les réponses qui vous disent d'utiliser $timeout. Vous ne pouvez pas savoir combien de temps la page mettra à se charger, ce n'est donc pas la meilleure solution.

Vous avez seulement besoin de savoir quand la page a fini de s'afficher.

<div ng-app='myApp'>
<div ng-controller="testctrl">
    <label>{{total}}</label>
    <table>
      <tr ng-repeat="item in items track by $index;" ng-init="end($index);">
        <td>{{item.number}}</td>
      </tr>
    </table>
</div>

var app = angular.module('myApp', ["testctrl"]);
var controllers = angular.module("testctrl", []);
 controllers.controller("testctrl", function($scope) {

  $scope.items = [{"number":"one"},{"number":"two"},{"number":"three"}];

  $scope.end = function(index){
  if(index == $scope.items.length -1
        && typeof $scope.endThis == 'undefined'){

            ///  DO STUFF HERE
      $scope.total = index + 1;
      $scop.endThis  = true;
 }
}
});

Suivez le ng-repeat par $index et quand la longueur du tableau est égale à l'index arrêtez la boucle et faites votre logique.

jsfiddle

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