A veces necesito usar $scope.$apply
en mi código y, a veces, arroja un error de "resumen ya en progreso". Así que comencé a encontrar una manera de evitar esto y encontré esta pregunta: AngularJS: Prevenga el error $ digest ya en progreso al llamar a $ scope. $ Apply () . Sin embargo, en los comentarios (y en la wiki angular) puedes leer:
No lo haga si (! $ Scope. $$ phase) $ scope. $ Apply (), significa que su $ scope. $ Apply () no es lo suficientemente alto en la pila de llamadas.
Entonces ahora tengo dos preguntas:
- ¿Por qué exactamente es esto un anti-patrón?
- ¿Cómo puedo usar $ scope. $ De forma segura?
Otra "solución" para evitar el error "resumen ya en progreso" parece estar usando $ timeout:
$timeout(function() {
//...
});
¿Es ese el camino a seguir? Es mas seguro? Entonces, aquí está la pregunta real: ¿Cómo puedo eliminar por completo la posibilidad de un error de "resumen ya en progreso"?
PD: solo estoy usando $ scope. $ Apply en devoluciones de llamada no angularjs que no son síncronas. (hasta donde yo sé, esas son situaciones en las que debe usar $ scope. $ apply si desea que se apliquen sus cambios)
fuente
scope
desde dentro de angular o desde fuera de angular. Entonces, de acuerdo con esto, siempre sabrá si necesita llamarscope.$apply
o no. Y si está usando el mismo código parascope
manipulación angular / no angular , lo está haciendo mal, siempre debe estar separado ... así que básicamente si se encuentra con un caso en el que necesita verificarscope.$$phase
, su código no es diseñado de manera correcta, y siempre hay una manera de hacerlo 'de la manera correcta'digest already in progress
errorRespuestas:
Después de investigar un poco más, pude resolver la pregunta de si siempre es seguro de usar
$scope.$apply
. La respuesta corta es sí.Respuesta larga:
Debido a la forma en que su navegador ejecuta Javascript, no es posible que dos llamadas de resumen colisionen por casualidad .
Por lo tanto, el error "resumen ya en progreso" solo puede ocurrir en una situación: cuando se emite un $ aplicar dentro de otro $ aplicar, por ejemplo:
Esta situación no puede surgir si usamos $ scope.apply en una devolución de llamada pura no angularjs, como por ejemplo la devolución de llamada de
setTimeout
. Por lo que el siguiente código es 100% a prueba de balas y no hay ninguna necesidad de hacer unaif (!$scope.$$phase) $scope.$apply()
incluso este es seguro:
Lo que NO es seguro (porque $ timeout, como todos los ayudantes de angularjs, ya lo llama
$scope.$apply
):Esto también explica por qué el uso de
if (!$scope.$$phase) $scope.$apply()
es un anti-patrón. Simplemente no lo necesita si lo usa$scope.$apply
de la manera correcta: en una devolución de llamada js pura como,setTimeout
por ejemplo.Lea http://jimhoskins.com/2012/12/17/angularjs-and-apply.html para obtener una explicación más detallada.
fuente
$document.bind('keydown', function(e) { $rootScope.$apply(function() { // a passed through function from the controller gets executed here }); });
No sé por qué tengo que hacer que $ aplicar aquí, porque estoy usando $ document.bind ..function $DocumentProvider(){ this.$get = ['$window', function(window){ return jqLite(window.document); }]; }
No hay aplicación allí.$timeout
semánticamente significa ejecutar código después de un retraso. Puede que sea funcionalmente seguro, pero es un truco. Debe haber una forma segura de usar $ apply cuando no pueda saber si un$digest
ciclo está en progreso o si ya está dentro de un$apply
.Definitivamente ahora es un anti-patrón. He visto explotar un resumen incluso si compruebas la fase $$. No se supone que acceda a la API interna indicada por
$$
prefijos.Deberías usar
ya que este es el método preferido en Angular ^ 1.4 y se expone específicamente como una API para la capa de aplicación.
fuente
En cualquier caso, cuando su resumen está en progreso y presiona otro servicio para que lo haga, simplemente da un error, es decir, el resumen ya está en progreso. así que para curar esto tienes dos opciones. puede comprobar si hay otro resumen en curso, como sondeos.
El primero
si la condición anterior es verdadera, entonces puede aplicar su $ scope. $ aplicar de otro modo y no
la segunda solución es usar $ timeout
no permitirá que el otro resumen comience hasta que $ timeout complete su ejecución.
fuente
$scope.$apply();
.$timeout
es la clave! Funciona y luego descubrí que también se recomienda.scope.$apply
desencadena un$digest
ciclo que es fundamental para el enlace de datos bidireccionalUn
$digest
ciclo busca objetos, es decir, modelos (para ser precisos$watch
) adjuntos para$scope
evaluar si sus valores han cambiado y si detecta un cambio, entonces toma los pasos necesarios para actualizar la vista.Ahora, cuando usa,
$scope.$apply
se enfrenta a un error "Ya en progreso", por lo que es bastante obvio que se está ejecutando un $ digest, pero ¿qué lo desencadenó?ans -> cada
$http
llamada, todo ng-click, repetir, mostrar, ocultar, etc. desencadenan un$digest
ciclo Y LA PEOR PARTE SE EJECUTA DE CADA $ ALCANCE.es decir, digamos que su página tiene 4 controladores o directivas A, B, C, D
Si tiene 4
$scope
propiedades en cada una de ellas, entonces tiene un total de 16 propiedades $ scope en su página.¡Si dispara
$scope.$apply
en el controlador D, un$digest
ciclo comprobará los 16 valores! más todas las propiedades de $ rootScope.Respuesta -> pero
$scope.$digest
activa un$digest
hijo y el mismo alcance, por lo que solo verificará 4 propiedades. Entonces, si está seguro de que los cambios en D no afectarán a A, B, C, use$scope.$diges
t not$scope.$apply
.Por lo tanto, un simple clic ng o ng-show / hide podría desencadenar un
$digest
ciclo en más de 100 propiedades, ¡incluso cuando el usuario no ha disparado ningún evento !fuente
Utilizar
$timeout
, es la forma recomendada.Mi escenario es que necesito cambiar elementos en la página según los datos que recibí de un WebSocket. Y como está fuera de Angular, sin $ timeout, se cambiará el único modelo, pero no la vista. Porque Angular no sabe que se ha cambiado ese dato.
$timeout
básicamente le está diciendo a Angular que haga el cambio en la siguiente ronda de $ digest.Intenté lo siguiente también y funciona. La diferencia para mí es que $ timeout es más claro.
fuente
$http
). De lo contrario, debe repetir este código por todas partes.$scope.$apply
si está utilizandosetTimeout
o$timeout
Encontré una solución muy buena:
inyecte eso donde lo necesite:
fuente