¿Cómo uso $scope.$watch y $scope.$apply en AngularJS?

No entiendo como usar $scope.$watch y $scope.$apply. La documentación oficial no es útil.

Lo que no entiendo específicamente:

  • ¿Están conectados al DOM?
  • ¿Cómo puedo actualizar los cambios de DOM en el modelo?
  • ¿Cuál es el punto de conexión entre ellos?

Lo intenté este tutorial, pero se necesita la comprensión de $watch y $apply por sentado

Qué hacer $apply y $watch hacer, y cómo los uso apropiadamente?

preguntado el 27 de febrero de 13 a las 12:02

6 Respuestas

Debe conocer cómo funciona AngularJS para comprenderlo.

Ciclo de resumen y $scope

En primer lugar, AngularJS define un concepto de lo que se denomina ciclo de digestión. Este ciclo se puede considerar como un bucle, durante el cual AngularJS comprueba si hay cambios en todas las variables. mirado por todos los $scopes. entonces si tienes $scope.myVar definido en su controlador y esta variable fue marcado para ser observado, entonces le está diciendo implícitamente a AngularJS que controle los cambios en myVar en cada iteración del bucle.

Una pregunta de seguimiento natural sería: ¿Está todo unido a $scope ¿Siendo vigilado? Afortunadamente, no. Si observara los cambios en cada objeto de su $scope, luego, rápidamente, un ciclo de resumen tardaría años en evaluarse y rápidamente se encontraría con problemas de rendimiento. Es por eso que el equipo de AngularJS nos dio dos formas de declarar algunos $scope variable como siendo observado (leer más abajo).

$watch ayuda a escuchar los cambios de $scope

Hay dos formas de declarar un $scope variable como siendo observado.

  1. Al usarlo en su plantilla a través de la expresión <span>{{myVar}}</span>
  2. Agregándolo manualmente a través del $watch excepcional

Anuncio 1) Este es el escenario más común y estoy seguro de que lo has visto antes, pero no sabías que esto ha creado un reloj en segundo plano. ¡Sí, lo tenía! Usando directivas AngularJS (como ng-repeat) también puede crear relojes implícitos.

Anuncio 2) Así es como creas el tuyo relojes. $watch El servicio lo ayuda a ejecutar algún código cuando se adjunta algún valor al $scope ha cambiado. Rara vez se usa, pero a veces es útil. Por ejemplo, si desea ejecutar algún código cada vez que cambia 'myVar', puede hacer lo siguiente:

function MyController($scope) {

    $scope.myVar = 1;

    $scope.$watch('myVar', function() {
        alert('hey, myVar has changed!');
    });

    $scope.buttonClicked = function() {
        $scope.myVar = 2; // This will trigger $watch expression to kick in
    };
}

$apply permite integrar cambios con el ciclo de resumen

Puedes pensar en el $apply funcionan como un mecanismo de integración. Verás, cada vez que cambias algo variable observada adjunta a la $scope object directamente, AngularJS sabrá que el cambio ha ocurrido. Esto se debe a que AngularJS ya sabía monitorear esos cambios. Entonces, si sucede en el código administrado por el marco, el ciclo de resumen continuará.

Sin embargo, a veces quieres cambiar algún valor fuera del mundo AngularJS y ver los cambios propagarse normalmente. Considere esto: usted tiene un $scope.myVar valor que se modificará dentro de un jQuery $.ajax() manipulador. Esto sucederá en algún momento en el futuro. AngularJS no puede esperar a que esto suceda, ya que no se le ha indicado que espere en jQuery.

Para abordar esto, $apply ha sido introducido. Le permite iniciar el ciclo de digestión de forma explícita. Sin embargo, solo debe usar esto para migrar algunos datos a AngularJS (integración con otros marcos), pero nunca use este método combinado con el código AngularJS normal, ya que AngularJS arrojará un error en ese momento.

¿Cómo se relaciona todo esto con el DOM?

Bueno, realmente deberías seguir el tutorial nuevamente, ahora que sabes todo esto. El ciclo de resumen se asegurará de que la interfaz de usuario y el código JavaScript permanezcan sincronizados, al evaluar cada observador adjunto a todos $scopes siempre y cuando nada cambie. Si no se producen más cambios en el bucle de resumen, se considera que ha finalizado.

Puede adjuntar objetos a la $scope objeto ya sea explícitamente en el Controlador, o declarándolos en {{expression}} formulario directamente en la vista.

Lecturas adicionales:

Respondido 31 ago 21, 06:08

¿Qué hay de usar esto? (método "Controlar como") - Leandro Bardelli

El uso de "Controlar como" no debería afectar a la información anterior. El uso de this.myVar pone myVar en el alcance. - Marcus Radell

@ŁukaszBachman - "entonces le está diciendo explícitamente a Angular que controle los cambios". Creo que es 'implícitamente' no 'explícitamente' - desaparecido en combate

En AngularJS, actualizamos nuestros modelos y nuestras vistas/plantillas actualizan el DOM "automáticamente" (a través de directivas integradas o personalizadas).

$apply y $watch, ambos métodos de Scope, no están relacionados con el DOM.

EL Conceptos La página (sección "Tiempo de ejecución") tiene una explicación bastante buena del bucle $digest, $apply, la cola $evalAsync y la lista $watch. Esta es la imagen que acompaña al texto:

$bucle de resumen

Cualquier código que tenga acceso a un alcance, normalmente controladores y directivas (sus funciones de enlace y/o sus controladores), puede configurar un "verExpresión" que AngularJS evaluará contra ese alcance. Esta evaluación ocurre cada vez que AngularJS ingresa a su bucle $digest (en particular, el bucle "$watch list"). Puede ver propiedades de alcance individuales, puede definir una función para ver dos propiedades juntas, puede ver la longitud de una matriz, etc.

Cuando las cosas suceden "dentro de AngularJS", por ejemplo, escribe en un cuadro de texto que tiene habilitado el enlace de datos bidireccional de AngularJS (es decir, usa el modelo ng), se dispara una devolución de llamada $http, etc. - Ya se ha llamado a $apply, por lo que Estás dentro del rectángulo "AngularJS" en la figura de arriba. Se evaluarán todas las watchExpressions (posiblemente más de una vez, hasta que no se detecten más cambios).

Cuando las cosas suceden "fuera de AngularJS", por ejemplo, usó bind() en una directiva y luego se activa ese evento, lo que da como resultado que se llame a su devolución de llamada, o que se activen algunas devoluciones de llamada registradas con jQuery, todavía estamos en el rectángulo "Nativo". Si el código de devolución de llamada modifica cualquier cosa que cualquier $watch esté viendo, llame a $apply para ingresar al rectángulo de AngularJS, lo que hace que se ejecute el bucle $digest y, por lo tanto, AngularJS notará el cambio y hará su magia.

Respondido 02 Feb 16, 08:02

Entiendo la idea, lo que no entiendo es cómo se transfieren realmente los datos. Tengo un modelo que es un objeto con muchos datos, uso algunos para manipular el DOM. entonces algo de eso se cambia. ¿Cómo coloco los datos modificados en el lugar correcto en el modelo? En el ejemplo que usé, él hace la manipulación y al final simplemente usa scope.$apply(scope.model), no entiendo qué datos se transfieren y cómo se transfieren al lugar correcto en el modelo. - Ilio

No se está produciendo ninguna transferencia mágica de datos. Normalmente, con las aplicaciones de Angular, debe cambiar los modelos de Angular, que luego impulsan las actualizaciones de vista/DOM. Si actualiza el DOM fuera de Angular, deberá actualizar manualmente los modelos. scope.$apply(scope.model) simplemente evaluará scope.model como una expresión angular, y luego ingrese un ciclo $digest. En el artículo al que hace referencia, probablemente scope.$apply() sería suficiente, ya que el modelo ya se está viendo. La función stop() está actualizando el modelo (creo que toUpdate es una referencia a scope.model), y luego se llama a $apply. - Marcos Rajcok

Parece que los documentos de AngularJS se han desplazado debajo de esta respuesta (el primer enlace no tiene "tiempo de ejecución" o $watch en la página, y el segundo enlace está roto -- a partir de ahora, de todos modos). Penosamente, las versiones de archivo no almacenó en caché el proceso asincrónico que creó el contenido. - ruffin

AngularJS extiende esto bucle de eventos, creando algo llamado AngularJS context.

$ reloj ()

Cada vez que vincula algo en la interfaz de usuario, inserta un $watch en un parche de $watch --.

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

Aquí tenemos $scope.user, que está vinculado a la primera entrada, y tenemos $scope.pass, que está ligado al segundo. Haciendo esto sumamos dos $watches a la $watch --.

Cuando nuestro plantilla está cargado, también conocido como en la fase de vinculación, el compilador buscará todas las directivas y creará todas las $watches que se necesitan.

AngularJS proporciona $watch, $watchcollection y $watch(true). A continuación se muestra un diagrama ordenado que explica los tres tomados de observadores en profundidad.

Ingrese la descripción de la imagen aquí

angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
  $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];

  $scope.$watch("users", function() {
    console.log("**** reference checkers $watch ****")
  });

  $scope.$watchCollection("users", function() {
    console.log("**** Collection  checkers $watchCollection ****")
  });

  $scope.$watch("users", function() {
    console.log("**** equality checkers with $watch(true) ****")
  }, true);

  $timeout(function(){
     console.log("Triggers All ")
     $scope.users = [];
     $scope.$digest();

     console.log("Triggers $watchCollection and $watch(true)")
     $scope.users.push({ name: 'Thalaivar'});
     $scope.$digest();

     console.log("Triggers $watch(true)")
     $scope.users[0].name = 'Superstar';
     $scope.$digest();
  });
}

http://jsfiddle.net/2Lyn0Lkb/

$digest loops

Cuando el navegador recibe un evento que puede ser administrado por el contexto AngularJS, el $digest se activará el bucle. Este lazo está hecho de dos lazos más pequeños. Uno procesa el $evalAsync cola, y el otro procesa el $watch list. $digest recorrerá la lista de $watch eso tenemos

app.controller('MainCtrl', function() {
  $scope.name = "vinoth";

  $scope.changeFoo = function() {
      $scope.name = "Thalaivar";
  }
});

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

Aquí solo tenemos uno $watch porque ng-click no crea ningún reloj.

Presionamos el botón.

  1. El navegador recibe un evento que ingresará al contexto AngularJS
  2. EL $digest loop se ejecutará y le preguntará a cada $watch por cambios.
  3. Puesto que el $watch que estaba buscando cambios en $scope.name informa un cambio, forzará otro $digest lazo.
  4. El nuevo bucle no informa nada.
  5. El navegador recupera el control y actualizará el DOM reflejando el nuevo valor de $scope.name
  6. Lo importante aquí es que CADA evento que ingrese al contexto de AngularJS ejecutará un $digest círculo. Eso significa que cada vez que escribimos una letra en una entrada, el bucle se ejecutará comprobando cada $watch en esta pagina

$ aplicar ()

Si llamas $apply cuando se activa un evento, pasará por el contexto angular, pero si no lo llama, se ejecutará fuera de él. Es tan fácil como eso. $apply llamará al $digest() loop internamente e iterará sobre todos los relojes para garantizar que el DOM se actualice con el valor recién actualizado.

EL $apply() El método activará a los observadores en todo el $scope cadena mientras que el $digest() El método solo activará los observadores en el actual $scope y su children. Cuando ninguno de los de arriba $scope los objetos necesitan saber acerca de los cambios locales, puede usar $digest().

Respondido el 20 de junio de 20 a las 10:06

Encontré videos muy detallados que cubren $watch, $apply, $digest y digerir ciclos en:

A continuación hay un par de diapositivas utilizadas en esos videos para explicar los conceptos (por si acaso, si los enlaces anteriores se eliminan o no funcionan).

Ingrese la descripción de la imagen aquí

En la imagen de arriba, "$scope.c" no se ve porque no se usa en ninguno de los enlaces de datos (en el marcado). Los otros dos ($scope.a y $scope.b) será vigilado.

Ingrese la descripción de la imagen aquí

De la imagen de arriba: según el evento del navegador respectivo, AngularJS captura el evento, realiza un ciclo de resumen (recorre todos los relojes en busca de cambios), ejecuta funciones de reloj y actualiza el DOM. Si no son eventos del navegador, el ciclo de resumen se puede activar manualmente usando $apply or $digest.

Más información sobre $apply y $digest:

Ingrese la descripción de la imagen aquí

Respondido el 07 de enero de 17 a las 15:01

Existen $watchGroup y $watchCollection también. Específicamente, $watchGroup es realmente útil si desea llamar a una función para actualizar un objeto que tiene múltiples propiedades en una vista que no es un objeto dom, por ejemplo, otra vista en el lienzo, WebGL o solicitud del servidor.

Aquí, la documentación aquí.

Respondido 19 Abr '20, 12:04

habría comentado sobre el $watchCollection pero veo que ya lo hiciste. Aquí está la documentación al respecto desde el sitio AngularJS. Proporcionan una imagen muy agradable de la $watch profundidad. Tenga en cuenta que la información está cerca de la parte inferior de la página. - JabberwockyDescompilador

Acabo de terminar de leer TODO lo anterior, aburrido y soñoliento (perdón pero es verdad). Muy técnico, profundo, detallado y seco. ¿Por qué estoy escribiendo? Debido a que AngularJS es masivo, muchos conceptos interconectados pueden volver loco a cualquiera. A menudo me preguntaba, ¿no soy lo suficientemente inteligente como para entenderlos? ¡No! Es porque muy pocos pueden explicar la tecnología en un lenguaje para tontos ¡sin todas las terminologías! Bien, déjame probar:

1) Todas son cosas impulsadas por eventos. (Oigo la risa, pero sigue leyendo)

Si no sabe qué es la función basada en eventos, entonces piense que coloca un botón en la página, conéctelo con una función usando "al hacer clic", esperando que los usuarios hagan clic en él para activar las acciones que plante dentro de la página. función. O piense en el "disparador" de SQL Server/Oracle.

2) $ reloj es "al hacer clic".

Lo que tiene de especial es que toma 2 funciones como parámetros, la primera da el valor del evento, la segunda toma el valor en consideración...

3) $digest es el jefe que revisa incansablemente, bla-bla-bla pero un buen jefe.

4) $apply te da el camino cuando quieres hacerlo manualmente, como una prueba de fallas (en caso de que el clic no se active, lo obliga a ejecutarse).

Ahora, hagámoslo visual. Imagínese esto para que sea aún más fácil captar la idea:

En un restaurante,

- MESERO

se supone que toman pedidos de los clientes, esto es

$watch(
  function(){return orders;},
  function(){Kitchen make it;}
);

- GERENTE corriendo para asegurarse de que todos los meseros estén despiertos, respondiendo a cualquier señal de cambio de los clientes. Esto es $digest()

- DUEÑO tiene el máximo poder para conducir a todos a pedido, esto es $apply()

Respondido el 20 de junio de 20 a las 10:06

Esto puede ser entendido por un niño de 5 años. Agradezco este tipo de respuesta. +1 - Chris22

"A menudo me preguntaba, ¿no soy lo suficientemente inteligente como para entenderlos? ¡No!"Disculpas. Me reí.- ruffin

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.