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

134

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 l' erreur: 10 itérations $ digest () atteintes. Abandon! erreur dans ma console.

J'ai créé jsfiddle pour la même chose.

Le prédicat de tri est initialisé uniquement à l'intérieur du ng-repeat et la limite est également appliquée sur le nombre d'objets. je pense donc que le fait d'avoir à la fois les observateurs sortby et limitTo est la raison de l'erreur.

Si le $ scope.reverse est faux (ordre croissant de score), alors il n'erreur.

Quelqu'un peut-il m'aider à comprendre ce qui ne va pas ici? Merci beaucoup pour votre aide.

Garçon grassouillet
la source
Si vous supprimez l'instruction if, est-ce toujours une erreur?
Mathew Berg
Merci pour votre réponse Mathew! J'ai mal diagnostiqué le problème. Le problème semble être lié aux filtres sortby et limitTo. J'ai mis à jour la question avec JSFiddle. Merci beaucoup pour votre aide.
Chubby Boy
c'est une chose angulaire. Vous devez mémoriser vos fonctions et vous souvenir de l'état. Attrapez ma réponse sur stackoverflow.com/questions/14376879/…
Julio Marins

Réponses:

116

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

Pour autant que je sache, il n'y a aucun problème avec le code que vous avez posté. L'erreur que vous avez mentionnée se produit normalement lorsque vous créez une boucle de modifications sur une propriété. Par exemple, comme lorsque vous surveillez les modifications d'une certaine propriété, puis que vous modifiez la valeur de cette propriété sur l'écouteur:

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

Cela entraînera un message d'erreur:

Erreur non interceptée: 10 itérations $ digest () atteintes. Abandon!
Les observateurs ont tiré au cours des 5 dernières itérations: ...

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

mettre à jour:

Voici votre problème:

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

Vous ne devez pas changer les objets / modèles pendant le rendu ou autrement, cela forcera un nouveau rendu (et par conséquent une boucle , ce qui provoquera l' erreur: 10 $ digest () itérations atteintes. Abandon! ' ).

Si vous souhaitez 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 le ng-initexactement pour éviter ce genre de situations:

Utilisez la directive ngInit dans les modèles (pour les applications jouets / exemples uniquement, non recommandé pour les applications réelles)

Voici un jsFiddle avec un exemple fonctionnel.

bmleite
la source
1
FYI, au lieu de angular.element(w).bind()vous pouvez utiliser element.bind().
Mark Rajcok
Merci beaucoup bmleite et Mark. J'ai mal diagnostiqué le problème. S'il vous plaît voir mes commentaires ci-dessus. J'ai mis à jour la question avec JSFiddle. Merci beaucoup pour votre aide.
Chubby Boy
Merci beaucoup @bmleite! J'essaie juste de comprendre plus. Mais pourquoi est-ce que si $ scope.reverse = false (ordre croissant du score) ne provoque pas d'erreur?
Chubby Boy
Pour monter et descendre, vous devez utiliser respectivement les préfixes «+» et «-» (c'est-à-dire «+ score» et «-score»). Plus d'informations à ce sujet ici .
bmleite
1
Cette réponse a résolu mon problème. L'explication est bonne en ce qu'elle décrit le problème: l'erreur d'itérations $ digest () se produit lorsque vous essayez de modifier une propriété en même temps que vous effectuez une boucle. Ainsi, chaque changement de propriété déclenche une mise à jour de la vue (UI) et angulaire finit par atteindre 10 boucles. L'exemple jsFiddle place le changement de propriété dans le Controller. La modification de la propriété dans la vue entraîne cette erreur.
mbokil
9

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

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

dans mon modèle

Cela provoque l'appel de la fonction myTrustSrc de mon contrôleur 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?

C'est la fonction du contrôleur

$scope.myTrustSrc = function(src) {
    return $sce.trustAsResourceUrl(src);
}
Natus Drew
la source
Je me demande toujours pourquoi la fonction est appelée plus d'une fois avec ng-src? - car angular essaie de créer le html plusieurs fois jusqu'à ce qu'il obtienne 2 résultats identiques. c'est le sens de l'erreur 10 itérations $ digest () atteintes, il a essayé 10 fois et n'a jamais obtenu 2 résultats identiques
bresleveloper
@bresleveloper pouvez-vous fournir un lien avec ces informations? Merci.
Willa
@Willa regardez ici docs.angularjs.org/api/ng/type/$rootScope.Scope CTRF + F this "La limite d'itération de réexécution est de 10 pour éviter un blocage de boucle infinie"
bresleveloper
Merci. Cela a aidé.
Willa
7

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

alors 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 du contrôleur sur my-dir était

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

qui quand j'ai fait

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

résolu le problème!

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

Michail Michailidis
la source
5

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

J'ai eu ce balisage avec plusieurs 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 où la propriété IsOpen correspond à la valeur booléenne dans le deuxième paramètre.

Dans le contrôleur, je définis $scope.datacomme ceci:

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

L'appel de la Where()méthode d'extension à partir 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 du balisage:

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

et le nouveau code contrôleur:

DataService.getData().then(function(results){
    $scope.openData = results.Where('IsOpen',true);
    $scope.closedData = results.Where('IsOpen',false);
});
squillman
la source
Cela ressemble exactement au problème que j'ai. Une fonction simple qui n'a rien mis à jour, mais qui a créé un tableau local, itéré sur un tableau $ scope existant et créé des objets dans le tableau local, puis renvoyé. En appelant une fois et en le stockant dans un membre $ scope, cela résout le problème. Merci. J'aimerais savoir quel est le problème avec l'utilisation directe de la méthode.
AgilePro
2

Assez tard pour la fête, mais mon problème se produisait car il y a un défaut dans l'interface utilisateur dans angular 1.5.8. Une chose à mentionner est que cette erreur n'est apparue que la première fois que j'exécutais l'application et qu'elle ne se reproduirait pas par la suite. Ce message de github a résolu mon problème. Fondamentalement, l'erreur implique $urlRouterProvider.otherwise("/home") La solution était une solution de contournement comme celle-ci:

$urlRouterProvider.otherwise(function($injector, $location) {
   var $state = $injector.get("$state");
   $state.go("your-state-for-home");
});
Allen
la source
2

Pour commencer, ignorez toutes les réponses avec vous dire d'utiliser $ watch. Angular fonctionne déjà sur un auditeur. Je vous garantis que vous compliquez les choses en pensant simplement dans ce sens.

Ignorez toutes les réponses qui vous indiquent à l'utilisateur $ timeout. Vous ne pouvez pas savoir combien de temps prendra le chargement de la page, ce n'est donc pas la meilleure solution.

Il vous suffit de savoir quand la page a terminé le rendu.

<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 lorsque la longueur du tableau est égale à l'index, arrêtez la boucle et faites votre logique.

jsfiddle

Jed Lynch
la source
1

J'ai eu cette erreur dans le contexte du contrôle d'arbre angulaire. Dans mon cas, c'était les options d'arbre. Je retournais treeOptions () à partir d'une fonction. Il retournait toujours le même objet. Mais Angular pense comme par magie que c'est un nouvel objet et provoque le démarrage d'un cycle de digestion. Provoquant une récursivité des résumés. La solution était de lier les treeOptions à la portée. Et attribuez-le une seule fois.

JDev
la source
1

C'est bizarre ... J'ai exactement la même erreur, venant d'une chose différente. Lorsque je crée mon contrôleur, j'ai passé le paramètre $ location, comme ceci:

App.controller('MessageController', function ($scope, $http, $log, $location, $attrs, MessageFactory, SocialMessageFactory) {
   // controller code
});

Cela s'est avéré être un bogue lorsque nous utilisons des bibliothèques tierces ou du pur JS pour manipuler certaines spécificités (ici window.location), le prochain condensé d'angular fera exploser cette erreur.

J'ai donc simplement supprimé $ location du paramètre de création du contrôleur, et cela a fonctionné à nouveau, sans cette erreur. Ou si vous avez absolument besoin d'utiliser le $ location de angular, vous devez supprimer chacun d'entre eux <a href="#">link</a>dans les liens de votre page modèle, et plutôt écrire href = "" . A travaillé pour moi.

J'espère que cela pourra aider un jour.

Alex
la source
0

Cela m'est arrivé juste après la mise à niveau de Firefox vers la version 51. Après clearing the cache, le problème a disparu.

Zsolti
la source
0

j'ai eu l'erreur similaire, car j'avais défini

ng-class = "GetLink ()"

au lieu de

ng-click = "GetLink ()"

Sonker Satish Kumar
la source
0

Cela m'est arrivé après la mise à niveau d'angular 1.6 -> 1.7 lors de l'utilisation de $ sce.trustAsResourceUrl () comme valeur de retour d'une fonction appelée depuis ng-src. Vous pouvez voir ce problème mentionné ici .

Dans mon cas, j'ai dû changer ce qui suit.

<source ng-src="{{trustSrc(someUrl)}}" type='video/mp4' />
trustSrc = function(url){
    return $sce.trustAsResourceUrl(url);
};

à

<source ng-src='{{trustedUrl}} type='video/mp4' />
trustedUrl = $sce.trustAsResourceUrl(someUrl);
aCMoFoCord
la source
0

J'ai eu exactement la même erreur en utilisant AngularJS 1.3.9 lorsque, dans mon filtre de tri personnalisé, j'ai appelé Array.reverse ()

Après l'avoir retiré, tout allait bien.

michael hansen079
la source