Error: 10 iteraciones $ digest () alcanzadas. Abortando! con predicado de ordenación dinámica

134

Tengo el siguiente código que se repite y muestra el nombre del usuario y su puntaje:

<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>

Y el controlador angular correspondiente.

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'}]
}

Cuando ejecuto el código anterior, aparece el Error: 10 $ digest () iteraciones alcanzadas. Abortando! error en mi consola

He creado jsfiddle para lo mismo.

El predicado sort se está inicializando solo dentro de ng-repeat y también se está aplicando el límite en el número de objetos. Por lo tanto, creo que tener los observadores sortby y limitTo juntos es la razón del error.

Si $ scope.reverse es falso (orden ascendente de puntuación), entonces no tiene error.

¿Alguien puede ayudarme a entender lo que está mal aquí? Agradezco mucho tu ayuda.

Gordito chico
fuente
Si elimina la instrucción if, ¿sigue siendo un error?
Mathew Berg
Gracias por tu respuesta Mathew! Diagnostiqué el problema incorrectamente. El problema parece ser con los filtros sortby y limitTo. He actualizado la pregunta con JSFiddle. Agradezco mucho tu ayuda.
Chubby Boy
Esto es algo angular. Debe memorizar sus funciones y luego recordar el estado. Vea mi respuesta en stackoverflow.com/questions/14376879/…
Julio Marins el

Respuestas:

116

Por favor revisa este jsFiddle . (El código es básicamente el mismo que publicó pero utilizo un elemento en lugar de la ventana para vincular los eventos de desplazamiento).

Hasta donde puedo ver, no hay ningún problema con el código que publicaste. El error que mencionó normalmente ocurre cuando crea un ciclo de cambios sobre una propiedad. Por ejemplo, como cuando observa cambios en una determinada propiedad y luego cambia el valor de esa propiedad en el oyente:

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

Esto dará como resultado un mensaje de error:

Error no capturado: 10 iteraciones $ digest () alcanzadas. Abortando!
Los observadores dispararon en las últimas 5 iteraciones: ...

Asegúrese de que su código no tenga este tipo de situaciones.

actualizar:

Este es tu problema:

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

No debe cambiar los objetos / modelos durante el renderizado o de lo contrario, forzará un nuevo render (y, en consecuencia, un bucle , lo que provoca el 'Error: 10 $ digest () iteraciones alcanzadas. Abortando!' ).

Si desea actualizar el modelo, hágalo en el Controlador o en una Directiva, nunca en la vista. documentación AngularJS recomienda no utilizar el ng-initexactamente para evitar este tipo de situaciones:

Use la directiva ngInit en plantillas (solo para aplicaciones de juguete / ejemplo, no se recomienda para aplicaciones reales)

Aquí hay un jsFiddle con un ejemplo de trabajo.

bmleite
fuente
1
Para su información, en lugar de angular.element(w).bind()usted puede usar element.bind().
Mark Rajcok
Muchas gracias bmleite y Mark. Diagnostiqué el problema incorrectamente. Por favor vea mis comentarios arriba. He actualizado la pregunta con JSFiddle. Agradezco mucho tu ayuda.
Chubby Boy
Muchas gracias @bmleite! Solo trato de entender más. Pero, ¿por qué si $ scope.reverse = false (orden ascendente de puntaje) no causa error?
Chubby Boy el
Para ascender y descender, debe usar los prefijos '+' y '-', respectivamente (es decir, '+ puntaje' y '-puntuación'). Más información sobre esto aquí .
bmleite
1
Esta respuesta resolvió mi problema. La explicación es buena, ya que describe el problema: el error de iteraciones $ digest () se produce cuando intenta modificar una propiedad al mismo tiempo que realiza un bucle. Por lo tanto, cada cambio de propiedad desencadena una actualización de la vista (UI) y angular eventualmente se maximiza en 10 bucles. El ejemplo jsFiddle coloca el cambio de propiedad en el controlador. Cambiar la propiedad en la vista da como resultado este error.
mbokil
9

La causa de este error para mí fue ...

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

en mi plantilla

Hace que la función myTrustSrc en mi controlador se llame en un bucle sin fin. Si elimino el ng-if de esta línea, entonces el problema está resuelto.

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

La función solo se llama algunas veces cuando no se usa ng-if. Todavía me pregunto por qué la función se llama más de una vez con ng-src?

Esta es la función en el controlador

$scope.myTrustSrc = function(src) {
    return $sce.trustAsResourceUrl(src);
}
Natus Drew
fuente
Todavía me pregunto por qué la función se llama más de una vez con ng-src? - porque angular intenta crear el html varias veces hasta obtener 2 resultados idénticos. ese es el significado de las iteraciones de error 10 $ digest () alcanzadas, lo intentó 10 veces y nunca obtuvo 2 resultados idénticos
bresleveloper
@bresleveloper, ¿podría proporcionar un enlace con esta información? Gracias.
Willa
@Willa mira aquí docs.angularjs.org/api/ng/type/$rootScope.Scope CTRF + F this "El límite de iteración de repetición es 10 para evitar un punto muerto de bucle infinito"
bresleveloper
Gracias. Eso ayudó.
Willa
7

Para mí fue que estaba pasando un resultado de función como entrada de enlace bidireccional '=' a una directiva que estaba creando un nuevo objeto cada vez.

así que tuve algo así:

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

y el método del controlador en my-dir era

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

que cuando hice a

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

¡resuelve el problema!

Espero que esto ayude a alguien :)

Michail Michailidis
fuente
5

Tengo otro ejemplo de algo que causó esto. Esperemos que ayude para futuras referencias. Estoy usando AngularJS 1.4.1.

Tuve este marcado con múltiples llamadas a una directiva personalizada:

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

myDataes una matriz y Where()es un método de extensión que itera sobre la matriz y devuelve una nueva matriz que contiene cualquier elemento del original donde la propiedad IsOpen coincide con el valor bool en el segundo parámetro.

En el controlador configuré $scope.dataasí:

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

El problema era llamar al Where()método de extensión desde la directiva como en el marcado anterior. Para solucionar este problema, moví la llamada al método de extensión al controlador en lugar del marcado:

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

y el nuevo código del controlador:

DataService.getData().then(function(results){
    $scope.openData = results.Where('IsOpen',true);
    $scope.closedData = results.Where('IsOpen',false);
});
squillman
fuente
Eso suena exactamente como el problema que tengo. Una función simple que no actualizaba nada, pero creaba una matriz local, iteraba sobre una matriz $ scope existente, creaba objetos en la matriz local y regresaba. Al llamar una vez y almacenarlo en un miembro $ scope resuelve el problema. Gracias. Ojalá supiera cuál es el problema con el uso directo del método.
AgilePro
2

Bastante tarde para la fiesta, pero mi problema estaba sucediendo porque hay un defecto en el enrutador ui en angular 1.5.8. Una cosa para mencionar es que este error apareció solo la primera vez que estaba ejecutando la aplicación y no volvería a ocurrir después. Esta publicación de github resolvió mi problema. Básicamente, el error involucra $urlRouterProvider.otherwise("/home") La solución fue una solución como esta:

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

Para empezar, ignore todas las respuestas con decirle que use $ watch. Angular ya funciona con un oyente. Te garantizo que estás complicando las cosas simplemente pensando en esta dirección.

Ignore todas las respuestas que le indican al usuario $ timeout. No puede saber cuánto tiempo llevará cargar la página, por lo tanto, esta no es la mejor solución.

Solo necesita saber cuándo la página ha terminado de renderizarse.

<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;
 }
}
});

Haga un seguimiento de ng-repeat por $ index y cuando la longitud de la matriz sea igual al índice, detenga el ciclo y haga su lógica.

jsfiddle

Jed Lynch
fuente
1

Recibí este error en el contexto del control de árbol angular. En mi caso fueron las opciones del árbol. Estaba devolviendo treeOptions () desde una función. Siempre estaba devolviendo el mismo objeto. Pero Angular piensa mágicamente que es un objeto nuevo y luego provoca que se inicie un ciclo de digestión. Causando una recursión de resúmenes. La solución fue unir las opciones del árbol al alcance. Y asígnelo solo una vez.

JDev
fuente
1

Es extraño ... Tengo exactamente el mismo error, proveniente de algo diferente. Cuando creo mi controlador, pasé el parámetro $ location, así:

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

Se demostró que esto es un error cuando usamos bibliotecas de terceros o JS puro para manipular algunos detalles (aquí window.location), el siguiente resumen de angular hará que se produzca este error.

Así que simplemente eliminé la ubicación $ del parámetro de creación del controlador, y funcionó nuevamente, sin este error. O si necesita usar la ubicación $ desde angular, debe eliminar cada uno <a href="#">link</a>de los enlaces de su página de plantilla, y escribir href = "" . Trabajó para mi.

Espero que pueda ayudar algún día.

Alex
fuente
0

Esto me sucedió justo después de actualizar Firefox a la versión 51. Después clearing the cache, el problema desapareció.

Zsolti
fuente
0

tuve el error similar, porque había definido

ng-class = "GetLink ()"

en vez de

ng-click = "GetLink ()"

Satish Kumar sonker
fuente
0

Esto me sucedió después de actualizar desde angular 1.6 -> 1.7 al usar $ sce.trustAsResourceUrl () como el valor de retorno de una función llamada desde ng-src. Puedes ver este problema mencionado aquí .

En mi caso tuve que cambiar lo siguiente.

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

a

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

Obtuve exactamente el mismo error al usar AngularJS 1.3.9 cuando, en mi filtro de clasificación personalizado, invoqué Array.reverse ()

Después de quitarlo, todo salió bien.

michael hansen079
fuente