AngularJS: differenza tra i metodi $ observ e $ watch


378

So che entrambi Watcherse Observerssono calcolati non appena qualcosa $scopecambia in AngularJS. Ma non riuscivo a capire quale fosse esattamente la differenza tra i due.

La mia comprensione iniziale è che Observerssono calcolate per espressioni angolari che sono condizioni sul lato HTML in cui vengono Watcherseseguite quando $scope.$watch()viene eseguita la funzione. Sto pensando correttamente?


1
La tua modifica non è utile e un po 'antagonizzante. Si prega di considerare gli altri che vengono qui per un vero aiuto.
smalone,

@smalone è cambiato. Grazie e scusa!
Abilash,

👍 Nessun problema. Grazie per il fissaggio.
smalone,

Risposte:


608

$ observ () è un metodosull'oggetto Attributes e, come tale, può essere utilizzato solo per osservare / osservare la modifica del valore di un attributo DOM. Viene utilizzato / chiamato solo all'interno delle direttive. Utilizzare $ observ quando è necessario osservare / guardare un attributo DOM che contiene interpolazione (ovvero {{}}).
Ad esempio,attr1="Name: {{name}}"e poi in una direttiva:attrs.$observe('attr1', ...).
(Se proviscope.$watch(attrs.attr1, ...), non funzionerà a causa dei {{}} - otterraiundefined.) Usa $ watch per tutto il resto.

$ watch () è più complicato. Può osservare / guardare una "espressione", in cui l'espressione può essere una funzione o una stringa. Se l'espressione è una stringa, è $ parse 'd (ovvero, valutata come espressione angolare ) in una funzione. (È questa funzione che si chiama ogni ciclo digest.) L'espressione stringa non può contenere {{}}. $ watch è un metodosull'oggetto Scope , quindi può essere usato / chiamato ovunque tu abbia accesso a un oggetto scope, quindi in

  • un controller - qualsiasi controller - uno creato tramite ng-view, ng-controller o un controller direttiva
  • una funzione di collegamento in una direttiva, poiché anche questa ha accesso a un ambito

Poiché le stringhe vengono valutate come espressioni angolari, $ watch viene spesso utilizzato quando si desidera osservare / guardare una proprietà modello / ambito. Ad esempio, attr1="myModel.some_prop"quindi in un controller o in una funzione di collegamento: scope.$watch('myModel.some_prop', ...)o scope.$watch(attrs.attr1, ...)(o scope.$watch(attrs['attr1'], ...)).
(Se provi attrs.$observe('attr1')otterrai la stringa myModel.some_prop, che probabilmente non è quello che vuoi.)

Come discusso nei commenti sulla risposta di @ PrimosK, tutti i $ osservati e $ orologi vengono controllati ad ogni ciclo digest .

Le direttive con ambiti isolati sono più complicate. Se viene utilizzata la sintassi "@", è possibile $ osservare o $ guardare un attributo DOM che contiene interpolazione (ovvero {{}}). (Il motivo per cui funziona con $ watch è perché la sintassi '@' fa l' interpolazione per noi, quindi $ watch vede una stringa senza {{}}.) Per rendere più facile ricordare quale usare quando, suggerisco di usare $ osservare anche per questo caso.

Per aiutare a testare tutto ciò, ho scritto un Plunker che definisce due direttive. Uno ( d1) non crea un nuovo ambito, l'altro ( d2) crea un ambito isolato. Ogni direttiva ha gli stessi sei attributi. Ogni attributo è sia $ osservato che $ osservato.

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

Guarda il registro della console per vedere le differenze tra $ observ e $ watch nella funzione di collegamento. Quindi fare clic sul collegamento e vedere quali $ osserva e $ orologi sono attivati ​​dalle modifiche alle proprietà apportate dal gestore dei clic.

Si noti che quando viene eseguita la funzione di collegamento, tutti gli attributi che contengono {{}} non vengono ancora valutati (quindi se si tenta di esaminare gli attributi, si ottiene undefined). L'unico modo per visualizzare i valori interpolati è utilizzare $ observ (o $ watch se si utilizza un ambito isolato con '@'). Pertanto, ottenere i valori di questi attributi è un'operazione asincrona . (Ed è per questo che abbiamo bisogno delle funzioni $ observ e $ watch.)

A volte non hai bisogno di $ osservare o $ guardare. Ad esempio, se il vostro attributo contiene un numero o un valore booleano (non una stringa), basta valutare una volta: attr1="22"e poi, per esempio, la funzione di collegamento: var count = scope.$eval(attrs.attr1). Se è solo una stringa costante - attr1="my string"- allora usa solo attrs.attr1nella tua direttiva (non c'è bisogno di $ eval ()).

Vedi anche il post del gruppo google di Vojta sulle espressioni $ watch.


13
Grande spiegazione! +1
PrimosK,

4
Bella risposta! Hai idea del perché ng-src/ng-hrefusare attr.$observeinvece di scope.$watchallora?
okm

4
+1 per il papa AngularJS! Ogni volta che cerco in Stack alcune informazioni sul mio ultimo problema angolare, finisco inevitabilmente per leggere la risposta accettata da @MarkRajcok.
GFoley83,

1
Grazie per l'ottimo post. scope. $ eval (oggetto) è davvero utile. Se l'elemento è una stringa json, viene convertito in un oggetto json.
bnguyen82,

5
@tamakisquare, sono intercambiabili quando si utilizza la @sintassi. Credo che non vi siano differenze di prestazioni (ma non ho esaminato il codice sorgente effettivo).
Mark Rajcok,

25

Se capisco bene la tua domanda, mi stai chiedendo qual è la differenza se registri il callback del listener $watcho se lo fai $observe.

Il callback registerd con $watchviene generato quando $digestviene eseguito.

Il callback registrato con $observeviene chiamato quando il valore cambia di attributi che contengono interpolazione (ad es attr="{{notJetInterpolated}}".).


All'interno della direttiva puoi usarli entrambi in modo molto simile:

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

o

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });

3
In realtà, poiché ogni modifica viene riflessa in $digestfase, è lecito ritenere che il $observecallback verrà richiamato $digest. E $watchverrà richiamato anche il callback, $digestma ogni volta che il valore viene modificato. Penso che facciano esattamente lo stesso lavoro: "guarda l'espressione, richiama il valore cambia". La differenza di parola chiave è probabilmente solo zucchero sintattico per non confondere lo sviluppatore.
Umur Kontacı,

1
@fastreload, sono totalmente d'accordo con il tuo commento .. Ben scritto!
PrimosK

@fastreload ... Grazie per la meravigliosa spiegazione. Se ho capito bene, gli osservatori sono per le espressioni angolari. Ho ragione?
Abilash,

@PrimosK: aggiungerti per il mio commento precedente.
Abilash,

2
Gli osservatori di @Abilash sono per guardare gli attributi dom, non solo le espressioni. Quindi, se modifichi il valore dell'attributo da solo, si rifletterà nel prossimo ciclo digest.
Umur Kontacı,

1

Penso che questo sia abbastanza ovvio:

  • $ observ viene utilizzato nella funzione di collegamento delle direttive.
  • $ watch viene utilizzato su scope per osservare qualsiasi cambiamento nei suoi valori.

Ricorda : entrambe le funzioni hanno due argomenti,

$observe/$watch(value : string, callback : function);
  • valore : è sempre un riferimento stringa all'elemento guardato (il nome della variabile di un ambito o il nome dell'attributo della direttiva da guardare)
  • callback : la funzione da eseguire del modulofunction (oldValue, newValue)

Ne ho fatto uno plunker, quindi puoi davvero avere una comprensione di entrambi il loro utilizzo. Ho usato l'analogia di Chameleon per rendere più semplice l'immagine.


2
È abbastanza ovvio sui suoi usi. Ma perché era la domanda. Mark lo ha riassunto magnificamente.
Abilash,

3
Penso che i parametri potrebbero essere cambiati - sembra passare newValue, quindi oldValue in attrs. $ Observ (). . .
blaster

0

Perché $ osserva è diverso da $ watch?

WatchExpression viene valutato e confrontato con il valore precedente di ciascun ciclo digest (), in caso di modifica del valore watchExpression, viene chiamata la funzione watch.

$ observ è specifico per la ricerca di valori interpolati. Se il valore dell'attributo di una direttiva è interpolato, ad esempio dir-attr="{{ scopeVar }}", la funzione di osservazione verrà chiamata solo quando è impostato il valore interpolato (e quindi quando $ digest ha già determinato che è necessario effettuare aggiornamenti). Fondamentalmente c'è già un osservatore per l'interpolazione, e la funzione $ observ lo trasporta.

Vedi $ observ & $ set in compile.js

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.