Pregunta ¿Cómo uso $ scope. $ Watch y $ scope. $ Apply en AngularJS?


No entiendo cómo 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 DOM al modelo?
  • ¿Cuál es el punto de conexión entre ellos?

Lo intenté este tutorial, pero toma la comprensión de $watch y $apply por supuesto.

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


1007
2018-02-27 12:50


origen


Respuestas:


Debe saber cómo funciona AngularJS para comprenderlo.

Ciclo Digest y $ scope

En primer lugar, AngularJS define un concepto de un llamado ciclo de resumen. Este ciclo se puede considerar como un ciclo, durante el cual AngularJS verifica si hay cambios en todas las variables mirado por todo el $scopes. Entonces si tienes $scope.myVar definido en su controlador y esta variable era marcado para ser visto, le está diciendo implícitamente a AngularJS que controle los cambios en myVar en cada iteración del ciclo.

Una pregunta natural de seguimiento sería: ¿Está todo unido a $scope ¿Siendo vigilado? Afortunadamente, no. Si observara los cambios en cada objeto en su $scope, entonces rápidamente un ciclo de digestión demorarí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 $scope variable según lo 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. Utilizándolo en su plantilla a través de la expresión <span>{{myVar}}</span>
  2. Al agregarlo manualmente a través del $watch Servicio

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 el fondo. Sí, lo hizo! Uso de directivas AngularJS (como ng-repeat) también puede crear relojes implícitos.

Anuncio 2) Así es como creas tu propio relojes. $watch el servicio le ayuda a ejecutar algún código cuando algún valor adjuntado a la $scope ha cambiado. Raramente se usa, pero a veces es útil. Por ejemplo, si desea ejecutar algún código cada vez que 'myVar' cambie, podría 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 función como de un mecanismo de integración. Ves, cada vez que cambias algo variable observada adjunta a la $scope objeto directamente, AngularJS sabrá que el cambio ha sucedido. Esto se debe a que AngularJS ya sabía supervisar 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 que los cambios se propagan normalmente Considera esto - tienes un $scope.myVar valor que se modificará dentro de un jQuery $.ajax()entrenador de animales. Esto sucederá en algún momento en el futuro. AngularJS no puede esperar a que esto suceda, ya que no se le indicó que espere en jQuery.

Para hacer frente a esto, $apply ha sido introducido. Le permite comenzar 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 regular, ya que AngularJS arrojará un error.

¿Cómo está todo esto relacionado con el DOM?

Bueno, deberías seguir el tutorial de nuevo, ahora que sabes todo esto. El ciclo de resumen se asegurará de que la IU y el código de JavaScript permanezcan sincronizados, evaluando a cada observador conectado a todos $scopes siempre y cuando nada cambie. Si no ocurren más cambios en el ciclo de digest, entonces se considera terminado.

Puede adjuntar objetos a la $scope objetar explícitamente en el Controlador o al declararlos en {{expression}} forma directamente en la vista.

Espero que eso ayude a aclarar algunos conocimientos básicos sobre todo esto.

Lecturas adicionales:


1648
2018-02-27 13:14



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

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

los Conceptos La página (sección "Runtime") tiene una explicación bastante buena del bucle $ digest, $ apply, la cola $ evalAsync y la lista $ watch. Aquí está la imagen que acompaña al texto:

$digest loop

Cualquiera que sea el código que tenga acceso a un alcance, normalmente los controladores y las directivas (sus funciones de enlace y / o sus controladores) pueden configurar un "watchExpression"que AngularJS evaluará contra ese alcance. Esta evaluación ocurre siempre que AngularJS ingresa su bucle de $ digest (en particular, el bucle de la" lista de $ $ "). Puede ver las propiedades de alcance individuales, puede definir una función para ver dos propiedades juntas. puedes ver la longitud de una matriz, etc.

Cuando suceden cosas "dentro de AngularJS" - por ejemplo, ingresa en un cuadro de texto que tiene habilitado el enlace de datos bidireccional AngularJS (es decir, utiliza ng-model), se ha invocado un $ http de devolución de llamada, etc. - $ apply, así que Está dentro del rectángulo "AngularJS" en la figura de arriba. Todos los watchExpressions se evaluarán (posiblemente más de una vez, hasta que no se detecten más cambios).

Cuando las cosas suceden "fuera de AngularJS", por ejemplo, utilizas bind () en una directiva y luego ese evento se activa, lo que hace que se llame de nuevo, o algunos incendios de devolución de llamada jQuery registrados, todavía estamos en el rectángulo "Nativo". Si el código de devolución de llamada modifica cualquier cosa que esté mirando cualquier $ watch, llame $ apply para ingresar al rectángulo de AngularJS, haciendo que se ejecute el bucle $ digest, y por lo tanto AngularJS notará el cambio y hará su magia.


152
2018-02-28 00:48



Este blog se ha cubierto todo lo que crea ejemplos y explicaciones comprensibles.

El AngularJS $scope funciones $watch(), $digest() y $apply() son algunas de las funciones centrales en AngularJS. Comprensión $watch(), $digest() y $apply() es esencial para entender AngularJS.

Cuando crea un enlace de datos desde algún punto de su vista a una variable en el objeto $ scope, AngularJS crea un "reloj" internamente. Un reloj significa que AngularJS mira los cambios en la variable en el $scope object. El marco es "observar" la variable. Los relojes se crean usando el $scope.$watch() función que cubriré más adelante en este texto.

En los puntos clave de su aplicación, AngularJS llama al $scope.$digest() función. Esta función itera a través de todos los relojes y comprueba si alguna de las variables observadas ha cambiado. Si una variable vigilada ha cambiado, se llama a una función oyente correspondiente. La función del oyente hace todo lo que necesita hacer, por ejemplo, cambiar un texto HTML para reflejar el nuevo valor de la variable observada. Por lo tanto, la $digest() función es lo que desencadena el enlace de datos para actualizar.

La mayoría de las veces, AngularJS llamará a $ scope. $ Watch () y $scope.$digest() funciones para usted, pero en algunas situaciones puede que tenga que llamarlas usted mismo. Por lo tanto, es realmente bueno saber cómo funcionan.

los $scope.$apply() función se utiliza para ejecutar algún código, y luego llamar $scope.$digest() después de eso, se verifican todos los relojes y se llaman las funciones de escucha correspondientes. los $apply() la función es útil al integrar AngularJS con otro código.

Entraré en más detalles sobre la $watch(), $digest() y $apply() funciones en el resto de este texto.

$ watch ()

los $scope.watch() función crea un reloj de alguna variable. Cuando registra un reloj pasa dos funciones como parámetros al $watch() función:

  • Una función de valor
  • Una función de oyente

Aquí hay un ejemplo:

$scope.$watch(function() {},
              function() {}
             );

La primera función es la función de valor y la segunda función es la función de oyente.

La función de valor debe devolver el valor que se está viendo. AngularJS puede verificar el valor devuelto contra el valor que la función de reloj devolvió la última vez. De esta forma, AngularJS puede determinar si el valor ha cambiado. Aquí hay un ejemplo:

$scope.$watch(function(scope) { return scope.data.myVar },
              function() {}
             );

Esta función de valor de ejemplo devuelve el $scope variable scope.data.myVar. Si el valor de esta variable cambia, se devolverá un valor diferente, y AngularJS llamará a la función del oyente.

Observe cómo la función de valor toma el alcance como parámetro (sin $ en el nombre). A través de este parámetro, la función de valor puede acceder al $scope y sus variables. La función de valor también puede ver variables globales en su lugar si lo necesita, pero con mayor frecuencia verá un $scope variable.

La función del oyente debe hacer lo que sea necesario si el valor ha cambiado. Quizás necesite cambiar el contenido de otra variable, o establecer el contenido de un elemento HTML o algo así. Aquí hay un ejemplo:

$scope.$watch(function(scope) { return scope.data.myVar },
              function(newValue, oldValue) {
                  document.getElementById("").innerHTML =
                      "" + newValue + "";
              }
             );

Este ejemplo establece el HTML interno de un elemento HTML en el nuevo valor de la variable, incrustado en el elemento b, lo que hace que el valor sea negrita. Por supuesto que podrías haber hecho esto usando el código {{ data.myVar }, pero esto es solo un ejemplo de lo que puedes hacer dentro de la función del oyente.

$ digest ()

los $scope.$digest() función itera a través de todos los relojes en el $scope object, y sus objetos $ scope del niño (si tiene alguno). Cuando $digest() itera sobre los relojes, llama a la función de valor para cada reloj. Si el valor devuelto por la función del valor es diferente al valor que devolvió la última vez que se llamó, se llama a la función del oyente para ese reloj.

los $digest() la función se llama siempre que AngularJS lo crea necesario. Por ejemplo, después de haber ejecutado un clic en el controlador de clic, o después de un AJAX devoluciones de llamadas (después de que se haya ejecutado la función de devolución de llamada done () / fail ()).

Puede encontrar algunos casos de esquina donde AngularJS no llama al $digest() función para ti. Por lo general, detectará eso al notar que los enlaces de datos no actualizan los valores mostrados. En ese caso, llame $scope.$digest() y debería funcionar O quizás puedas usar $scope.$apply() en cambio, lo explicaré en la siguiente sección.

$ aplicar ()

los $scope.$apply() función toma una función como parámetro que se ejecuta, y después de eso $scope.$digest() se llama internamente Eso hace que sea más fácil para usted asegurarse de que todos los relojes estén marcados y, por lo tanto, todos los enlaces de datos actualizados. Aquí hay un $apply() ejemplo:

$scope.$apply(function() {
    $scope.data.myVar = "Another value";
});

La función pasó a la $apply() función como parámetro cambiará el valor de $scope.data.myVar. Cuando la función sale de AngularJS llamará al $scope.$digest() función para que todos los relojes se verifiquen para ver si hay cambios en los valores observados.

Ejemplo

Para ilustrar cómo $watch(), $digest() y $apply() funciona, mira este ejemplo:

<div ng-controller="myController">
    {{data.time}}

    <br/>
    <button ng-click="updateTime()">update time - ng-click</button>
    <button id="updateTimeButton"  >update time</button>
</div>


<script>
    var module       = angular.module("myapp", []);
    var myController1 = module.controller("myController", function($scope) {

        $scope.data = { time : new Date() };

        $scope.updateTime = function() {
            $scope.data.time = new Date();
        }

        document.getElementById("updateTimeButton")
                .addEventListener('click', function() {
            console.log("update time clicked");
            $scope.data.time = new Date();
        });
    });
</script>

su ejemplo ata el $scope.data.time variable a una directiva de interpolación que combina el valor de la variable en la página HTML. Este enlace crea un reloj interno en el $scope.data.time variable.

El ejemplo también contiene dos botones. El primer botón tiene una ng-click oyente unido a ella. Cuando se hace clic en ese botón, $scope.updateTime() se llama a la función, y después de eso llamadas AngularJS $scope.$digest() para que los enlaces de datos se actualicen

El segundo botón obtiene un detector de eventos de JavaScript estándar adjunto desde la función del controlador. Cuando se hace clic en el segundo botón, se ejecuta la función del oyente. Como puede ver, las funciones del oyente para ambos botones hacen casi lo mismo, pero cuando se llama a la función del oyente del segundo botón, el enlace de datos no se actualiza. Eso es porque el $scope.$digest() no se llama después de que se ejecuta el detector de eventos del segundo botón. Por lo tanto, si hace clic en el segundo botón, la hora se actualiza en $scope.data.time variable, pero la nueva hora nunca se muestra.

Para arreglar eso, podemos agregar un $scope.$digest() llamar a la última línea del oyente de eventos del botón, como este:

document.getElementById("updateTimeButton")
        .addEventListener('click', function() {
    console.log("update time clicked");
    $scope.data.time = new Date();
    $scope.$digest();
});

En lugar de llamar $digest() dentro de la función de escucha de botón también podría haber utilizado el $apply() funciona así:

document.getElementById("updateTimeButton")
        .addEventListener('click', function() {
    $scope.$apply(function() {
        console.log("update time clicked");
        $scope.data.time = new Date();
    });
});

Observe cómo el $scope.$apply() función se llama desde el oyente de eventos de botón, y cómo la actualización de la $scope.data.timevariable se realiza dentro de la función pasada como parámetro al $apply() función. Cuando el $apply() la llamada a la función finaliza llamadas AngularJS $digest() internamente, por lo que todos los enlaces de datos se actualizan.


53
2018-02-22 10:13



AngularJS extiende esto eventos-loop, creando algo llamado AngularJS context.

$ watch ()

Cada vez que enlaza algo en la interfaz de usuario inserta un $watch en un $watch lista.

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, agregamos dos $watches a la $watch lista.

Cuando nuestra modelo está cargado, AKA en la fase de enlace, el compilador buscará cada directiva y creará todas las $watches que son necesarios.

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

Enter image description here

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 lazo

Cuando el navegador recibe un evento que puede ser manejado por el contexto AngularJS, $digest loop se disparará. Este bucle está hecho de dos bucles más pequeños. Uno procesa el $evalAsync cola, y el otro procesa el $watch list. los $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í tenemos solo una $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. los $digest loop se ejecutará y le preguntará a cada $ watch por los cambios.
  3. Desde el $watch que estaba buscando cambios en $ scope.name informa un cambio, forzará a otro $digest lazo.
  4. El nuevo bucle no informa nada.
  5. El navegador recupera el control y actualizará el DOM que refleja el nuevo valor de $ scope.name
  6. Lo importante aquí es que CADA evento que entre en el contexto de AngularJS ejecutará un $digest lazo. Eso significa que cada vez que escribimos una letra en una entrada, el ciclo se ejecutará comprobando cada $watch en esta página.

$ aplicar ()

Si llamas $apply cuando se dispara un evento, pasará por el contexto angular, pero si no lo llamas, se ejecutará fuera de él. Es tan fácil como eso. $apply llamará al $digest() bucle interno y se repetirá en todos los relojes para garantizar que el DOM se actualice con el valor actualizado recientemente.

los $apply() método desencadenará vigilantes en la totalidad $scope cadena mientras que el $digest() método solo activará los observadores en la corriente $scope y es children. Cuando ninguno de los superiores $scope los objetos necesitan saber sobre los cambios locales, puedes usar $digest().


37
2018-04-22 13:06



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 ej. otra vista en lienzo, webGL o solicitud del servidor. Aquí, la documentación enlazar.


17
2018-03-18 10:50



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

Las siguientes son algunas de las diapositivas utilizadas en esos videos para explicar los conceptos (por si acaso, si los enlaces anteriores se eliminan / no funcionan).

Enter image description here

En la imagen de arriba, "$ scope.c" no se está viendo ya que no se usa en ninguno de los enlaces de datos (en el marcado). Los otros dos ($scope.a y $scope.b) será observado.

Enter image description here

De la imagen de arriba: Basado en el evento del navegador respectivo, AngularJS captura el evento, realiza un ciclo de resumen (revisa todos los relojes para ver los cambios), ejecuta funciones de reloj y actualiza el DOM. Si no se trata de eventos del navegador, el ciclo de resumen puede activarse manualmente mediante $apply o $digest.

Más sobre $apply y $digest:

Enter image description here


15
2017-11-20 16:28



Simplemente termine de leer TODO lo anterior, aburrido y somnoliento (lo siento, pero es cierto). Muy técnico, en profundidad, detallado y seco. ¿Por qué estoy escribiendo? Debido a que AngularJS es masivo, muchos conceptos interconectados pueden hacer que cualquiera se vuelva loco. A menudo me preguntaba, ¿no soy lo suficientemente inteligente como para entenderlos? ¡No! Es porque muy pocos pueden explicar la tecnología en una lenguaje para-dummie sin todas las terminologías! De acuerdo, déjame intentarlo:

1) Son todas las cosas impulsadas por eventos. (Escucho la risa, pero sigo leyendo)

Si no sabe qué es lo que sucede en un evento, entonces   piensas que colocas un botón   en la página, conéctelo con una función usando "on-click", esperando   usuarios para hacer clic en él para activar las acciones que planta dentro del   función. O piense en el "desencadenador" de SQL Server / Oracle.

2) $ watch es "on-click". 

Lo especial es que toma 2 funciones como parámetros, primero   da el valor del evento, el segundo toma el valor en   consideración...

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

4) $ apply te da la forma cuando quieres hacerlo manualmente, como a prueba de fallas (en caso de que el clic no se active, lo fuerza a ejecutar).

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

En un restaurante, 

- WAITERS se supone que deben recibir órdenes de los clientes, esto es

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

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

- PROPIETARIO tiene el máximo poder para conducir a todos a petición, esto es $apply()


10
2018-02-07 01:20