w3schools not example already angularjs angularjs-scope

not - $watch angularjs w3schools



¿Por qué usar if(! $ Scope. $$ phase) $ scope. $ Apply() un anti-patrón? (6)

A veces necesito usar $scope.$apply en mi código y a veces arroja un error "resumen ya en progreso". Así que comencé a encontrar una forma de evitar esto y encontré esta pregunta: AngularJS: Prevent error $ digest ya está 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 $ scope. $ Apply () no es lo suficientemente alto en la pila de llamadas.

Entonces ahora tengo dos preguntas:

  1. ¿Por qué exactamente esto es un antipatrón?
  2. ¿Cómo puedo usar $ scope $ $ de forma segura?

Otra "solución" para evitar el error "digerir ya en progreso" parece estar usando $ timeout:

$timeout(function() { //... });

¿Es ese el camino a seguir? ¿Es más seguro? Así que aquí está la verdadera pregunta: ¿cómo puedo eliminar por completo la posibilidad de un error de "resumen ya en curso"?

PD: solo estoy usando $ scope. $ Apply en devoluciones de llamada no angulares que no son síncronas. (Hasta donde sé, esas son situaciones en las que debe usar $ scope. $ apply si desea que se apliquen sus cambios)


Definitivamente es un antipatrón ahora. He visto explotar un resumen incluso si compruebas la fase $$. Se supone que no debes acceder a la API interna indicada por los prefijos $$ .

Deberías usar

$scope.$evalAsync();

ya que este es el método preferido en Angular ^ 1.4 y está específicamente expuesto como una API para la capa de aplicación.


Después de excavar algo más, pude resolver la cuestión de si siempre es seguro 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 digerir colisionen por casualidad .

El código JavaScript que escribimos no se ejecuta de una vez, sino que se ejecuta por turnos. Cada uno de estos giros se ejecuta sin interrupción de principio a fin, y cuando se está ejecutando un turno, no sucede nada más en nuestro navegador. (de http://jimhoskins.com/2012/12/17/angularjs-and-apply.html )

Por lo tanto, el error "resumen ya en progreso" solo puede ocurrir en una situación: cuando se emite $ apply dentro de otra $ apply, por ejemplo:

$scope.apply(function() { // some code... $scope.apply(function() { ... }); });

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 . Entonces, el siguiente código es 100% a prueba de balas y no hay necesidad de hacer un if (!$scope.$$phase) $scope.$apply()

setTimeout(function () { $scope.$apply(function () { $scope.message = "Timeout called!"; }); }, 2000);

incluso este es seguro:

$scope.$apply(function () { setTimeout(function () { $scope.$apply(function () { $scope.message = "Timeout called!"; }); }, 2000); });

Lo que NO es seguro (porque $ timeout - como todos los auxiliares angulares - ya llama a $scope.$apply para usted):

$timeout(function () { $scope.$apply(function () { $scope.message = "Timeout called!"; }); }, 2000);

Esto también explica por qué el uso de if (!$scope.$$phase) $scope.$apply() es un anti-patrón. Simplemente no lo necesita si usa $scope.$apply setTimeout 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.


En cualquier caso, cuando su digest se encuentra en progreso y usted presiona otro servicio para que lo digiera, simplemente da un error, es decir, un resumen que ya está en progreso. entonces para curar esto tienes dos opciones. puede verificar si hay algún otro producto en progreso, como el sondeo.

El primero

if ($scope.$root.$$phase != ''$apply'' && $scope.$root.$$phase != ''$digest'') { $scope.$apply(); }

si la condición anterior es verdadera, entonces puede aplicar su $ scope. $ apply otherwies no y

la segunda solución es use $ timeout

$timeout(function() { //... })

no permitirá que el otro resumen comience hasta que $ timeout complete su ejecución.


Encontré una solución genial

.factory(''safeApply'', [function($rootScope) { return function($scope, fn) { var phase = $scope.$root.$$phase; if (phase == ''$apply'' || phase == ''$digest'') { if (fn) { $scope.$eval(fn); } } else { if (fn) { $scope.$apply(fn); } else { $scope.$apply(); } } } }])

Inyecta eso donde necesites:

.controller(''MyCtrl'', [''$scope'', ''safeApply'', function($scope, safeApply) { safeApply($scope); // no function passed in safeApply($scope, function() { // passing a function in }); } ])


Use $timeout , es el camino recomendado.

Mi caso 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 esa información se ha cambiado. $timeout es básicamente decirle a Angular que haga el cambio en la próxima ronda de $ digest.

Intenté lo siguiente también y funciona. La diferencia para mí es que $ timeout es más claro.

setTimeout(function(){ $scope.$apply(function(){ // changes }); },0)


scope.$apply desencadena un ciclo $digest que es fundamental para el enlace de datos bidireccional

Un ciclo de $digest comprueba los objetos, es decir, los modelos (para ser precisos $watch ) adjuntos a $scope para evaluar si sus valores han cambiado y si detecta un cambio, toma los pasos necesarios para actualizar la vista.

Ahora cuando usas $scope.$apply te enfrentas a un error "Ya está en progreso", por lo que es bastante obvio que se está ejecutando un $ digest pero ¿qué lo desencadenó?

ans -> cada $http llamadas, todo ng-clic, repetir, mostrar, ocultar etc. desencadenan un ciclo $digest Y LA PEOR PARTE QUE FUNCIONA DE CADA $ SCOPE.

es decir, su página tiene 4 controladores o directivas A, B, C, D

Si tiene 4 $scope properties en cada una de ellas, tiene un total de 16 $ scope properties en su página.

¡Si activa $scope.$apply en el controlador D, entonces un ciclo de $digest revisará los 16 valores! más todas las propiedades $ rootScope.

Answer -> but $scope.$digest desencadena un $digest en el hijo y el mismo alcance, por lo que solo se verifican 4 propiedades. Entonces, si está seguro de que los cambios en D no afectarán a A, B, C, utilice $scope.$diges t not $scope.$apply $scope.$diges .

Por lo tanto, un simple ng-click o ng-show / hide podría estar desencadenando un ciclo $digest en más de 100 propiedades incluso cuando el usuario no haya disparado ningún evento .