AngularJS: diferencia entre los métodos $ observe y $ watch

378

Sé que ambos Watchersy Observersse calculan tan pronto como algo $scopecambia en AngularJS. Pero no podía entender cuál es exactamente la diferencia entre los dos.

Mi comprensión inicial es que Observersse calculan para expresiones angulares que son condiciones en el lado HTML donde se Watchersejecutan cuando $scope.$watch()se ejecuta la función. ¿Estoy pensando correctamente?

Abilash
fuente
1
Su edición no es útil y un poco antagonista. Por favor sea considerado con los demás que vienen aquí para recibir ayuda.
pequeña
@smalone cambió. Gracias y perdon!
Abilash
👍 No te preocupes. Gracias por arreglarlo.
Pequeño

Respuestas:

608

$ observe () es un método en elobjeto Atributos y, como tal, solo se puede usar para observar / observar el cambio de valor de un atributo DOM. Solo se usa / llama dentro de las directivas. Use $ observe cuando necesite observar / mirar un atributo DOM que contiene interpolación (es decir, {{}} 's).
Por ejemplo,attr1="Name: {{name}}"y luego en una directiva:attrs.$observe('attr1', ...).
(Si lo intentasscope.$watch(attrs.attr1, ...)no funcionará debido a los {{}} s, obtendrásundefined). Usa $ watch para todo lo demás.

$ watch () es más complicado. Puede observar / mirar una "expresión", donde la expresión puede ser una función o una cadena. Si la expresión es una cadena, es $ parse 'd (es decir, evaluada como una expresión angular ) en una función. (Es esta función la que se llama cada ciclo de resumen.) La expresión de cadena no puede contener {{}}. $ watch es un método en elobjeto Scope , por lo que se puede usar / invocar donde tenga acceso a un objeto Scope , por lo tanto, en

  • un controlador, cualquier controlador, uno creado a través de ng-view, ng-controller o un controlador directivo
  • una función de enlace en una directiva, ya que también tiene acceso a un alcance

Debido a que las cadenas se evalúan como expresiones angulares, $ watch se usa a menudo cuando desea observar / mirar una propiedad de modelo / alcance. Por ejemplo, attr1="myModel.some_prop"luego en una función de controlador o enlace: scope.$watch('myModel.some_prop', ...)o scope.$watch(attrs.attr1, ...)(o scope.$watch(attrs['attr1'], ...)).
(Si lo intentas attrs.$observe('attr1'), obtendrás la cadena myModel.some_prop, que probablemente no sea lo que deseas).

Como se discutió en los comentarios sobre la respuesta de @ PrimosK, todos los $ observa y los $ relojes se verifican en cada ciclo de resumen .

Las directivas con ámbitos aislados son más complicadas. Si se usa la sintaxis '@', puede $ observar o $ mirar un atributo DOM que contiene interpolación (es decir, {{}} 's). (La razón por la que funciona con $ watch es porque la sintaxis '@' hace la interpolación por nosotros, por lo tanto, $ watch ve una cadena sin {{}} 's.) Para que sea más fácil recordar cuál usar cuándo, sugiero usar $ observar para este caso también.

Para ayudar a probar todo esto, escribí un Plunker que define dos directivas. Uno ( d1) no crea un nuevo alcance, el otro ( d2) crea un alcance aislado. Cada directiva tiene los mismos seis atributos. Cada atributo es tanto $ obser'd como $ watch'ed.

<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
        attr5="a_string" attr6="{{1+aNumber}}"></div>

Mire el registro de la consola para ver las diferencias entre $ observe y $ watch en la función de enlace. Luego haga clic en el enlace y vea qué $ observa y $ relojes son activados por los cambios de propiedad realizados por el controlador de clics.

Tenga en cuenta que cuando se ejecuta la función de enlace, los atributos que contienen {{}} aún no se evalúan (por lo tanto, si intenta examinar los atributos, obtendrá undefined). La única forma de ver los valores interpolados es usar $ observe (o $ watch si usa un alcance aislado con '@'). Por lo tanto, obtener los valores de estos atributos es una operación asincrónica . (Y es por eso que necesitamos las funciones $ observe y $ watch).

A veces no necesitas $ observar o $ mirar. Por ejemplo, si el atributo contiene un número o un valor lógico (no una cadena), justo evaluar una vez: attr1="22"y, a continuación, digamos, en su función de vinculación: var count = scope.$eval(attrs.attr1). Si se trata solo de una cadena constante, attr1="my string"simplemente utilícela attrs.attr1en su directiva (no necesita $ eval ())

Vea también la publicación del grupo de Google de Vojta sobre las expresiones $ watch.

Mark Rajcok
fuente
13
¡Gran explicación! +1
PrimosK
44
¡Gran respuesta! ¿Tienes idea de por qué ng-src/ng-hrefusar en attr.$observelugar de scope.$watchentonces?
okm
44
+1 ¡Para el Papa AngularJS! Cada vez que busco información en Stack sobre mi último problema angular, inevitablemente termino leyendo la respuesta aceptada de @MarkRajcok.
GFoley83
1
Gracias por un gran post. scope. $ eval (item) es realmente útil. Si el elemento es una cadena json, se convierte en un objeto json.
bnguyen82
55
@tamakisquare, son intercambiables cuando se usa la @sintaxis. Creo que no hay diferencia de rendimiento (pero no he mirado el código fuente real).
Mark Rajcok
25

Si entiendo bien su pregunta, está preguntando cuál es la diferencia si registra la devolución de llamada del oyente $watcho si lo hace $observe.

La devolución de llamada registrada con $watchse dispara cuando $digestse ejecuta.

La devolución de llamada registrada con $observese llama cuando el valor cambia de atributos que contienen interpolación (por ejemplo attr="{{notJetInterpolated}}")


Dentro de la directiva, puede usar ambos de manera muy similar:

    attrs.$observe('attrYouWatch', function() {
         // body
    });

o

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });
PrimosK
fuente
3
En realidad, dado que cada cambio se refleja en la $digestfase, es seguro asumir que $observese llamará a la devolución de llamada $digest. Y la $watchdevolución de llamada también se llamará, $digestpero siempre que se cambie el valor. Creo que hacen exactamente el mismo trabajo: "ver la expresión, devolver la llamada, el valor cambia". La diferencia de palabras clave es posiblemente solo el azúcar sintáctico para no confundir al desarrollador.
Umur Kontacı
1
@fastreload, estoy totalmente de acuerdo con tu comentario ... ¡Muy bien escrito!
PrimosK
@fastreload ... Gracias por la maravillosa explicación. Si entendí correctamente, los observadores son para expresiones angulares. Estoy en lo cierto?
Abilash
@PrimosK: añadiéndote a mi comentario anterior.
Abilash
2
Los observadores de @Abilash son para observar atributos dom, no solo expresiones. Entonces, si cambia el valor del atributo usted mismo, se reflejará en el próximo ciclo de resumen.
Umur Kontacı
1

Creo que esto es bastante obvio:

  • $ observe se utiliza para vincular la función de las directivas.
  • $ watch se usa en el alcance para observar cualquier cambio en sus valores.

Tenga en cuenta : tanto la función tiene dos argumentos,

$observe/$watch(value : string, callback : function);
  • valor : siempre es una referencia de cadena al elemento observado (el nombre de la variable de un alcance o el nombre del atributo de la directiva que se debe observar)
  • devolución de llamada : la función a ejecutar del formulariofunction (oldValue, newValue)

He hecho un plunker, por lo que realmente puede comprender tanto su utilización. He usado la analogía de Chameleon para que sea más fácil de imaginar.

vdegenne
fuente
2
Es bastante obvio acerca de sus usos. Pero por qué era la pregunta. Mark lo ha resumido maravillosamente.
Abilash
3
Creo que los parámetros pueden cambiarse: parece pasar newValue, luego oldValue a attrs. $ Observe (). . .
Blaster
0

¿Por qué $ observe es diferente de $ watch?

WatchExpression se evalúa y se compara con el valor anterior de cada ciclo de digest (), si hay un cambio en el valor de watchExpression, se llama a la función watch.

$ observe es específico para observar valores interpolados. Si el valor de atributo de una directiva se interpola, por ejemplo dir-attr="{{ scopeVar }}", la función de observación solo se llamará cuando se establezca el valor interpolado (y, por lo tanto, cuando $ digest ya haya determinado que se deben realizar actualizaciones). Básicamente ya hay un observador para la interpolación, y la función $ observe se complementa con eso.

Vea $ observe & $ set en compile.js

Niko
fuente