AngularJS: Différence entre les méthodes $ observe et $ veille

378

Je sais que les deux Watcherset Observerssont calculés dès que quelque chose $scopechange dans AngularJS. Mais je ne pouvais pas comprendre quelle est exactement la différence entre les deux.

Ma compréhension initiale est que Observerssont calculées pour les expressions angulaires qui sont des conditions du côté HTML où elles sont Watchersexécutées lorsque la $scope.$watch()fonction est exécutée. Suis-je en train de penser correctement?

Abilash
la source
1
Votre montage n'est pas utile et un peu antagoniste. Veuillez être attentif aux autres qui viennent ici pour une aide réelle.
smalone
@smalone a changé. Merci et désolé!
Abilash
👍 Pas de soucis. Merci d'avoir réparé.
smalone

Réponses:

608

$ observe () est une méthode sur l'objet Attributs , et en tant que telle, elle ne peut être utilisée que pour observer / surveiller le changement de valeur d'un attribut DOM. Il est uniquement utilisé / appelé directives internes. Utilisez $ observe lorsque vous devez observer / surveiller un attribut DOM qui contient une interpolation (c'est-à-dire {{}}).
Par exemple,attr1="Name: {{name}}"puis dans une directive:attrs.$observe('attr1', ...).
(Si vous essayez,scope.$watch(attrs.attr1, ...)cela ne fonctionnera pas à cause des {{}} - vous obtiendrezundefined.) Utilisez $ watch pour tout le reste.

$ watch () est plus compliqué. Il peut observer / regarder une "expression", où l'expression peut être soit une fonction, soit une chaîne. Si l'expression est une chaîne, elle est $ parse 'd (c'est-à-dire évaluée comme une expression angulaire ) dans une fonction. (C'est cette fonction qui est appelée à chaque cycle de résumé.) L'expression de chaîne ne peut pas contenir de {{}}. $ watch est une méthode sur l'objet Scope , elle peut donc être utilisée / appelée partout où vous avez accès à un objet Scope , donc dans

  • un contrôleur - n'importe quel contrôleur - créé via ng-view, ng-controller ou un contrôleur directif
  • une fonction de liaison dans une directive, car elle a également accès à un champ d'application

Étant donné que les chaînes sont évaluées en tant qu'expressions angulaires, $ watch est souvent utilisé lorsque vous souhaitez observer / surveiller une propriété de modèle / portée. Par exemple,, attr1="myModel.some_prop"puis dans une fonction de contrôleur ou de liaison: scope.$watch('myModel.some_prop', ...)ou scope.$watch(attrs.attr1, ...)(ou scope.$watch(attrs['attr1'], ...)).
(Si vous essayez, attrs.$observe('attr1')vous obtiendrez la chaîne myModel.some_prop, ce qui n'est probablement pas ce que vous voulez.)

Comme discuté dans les commentaires sur la réponse de @ PrimosK, tous les $ observes et $ watch sont vérifiés à chaque cycle de résumé .

Les directives avec des étendues isolées sont plus compliquées. Si la syntaxe '@' est utilisée, vous pouvez $ observer ou $ regarder un attribut DOM qui contient une interpolation (c'est-à-dire {{}}). (La raison pour laquelle il fonctionne avec $ watch est parce que la syntaxe '@' fait l' interpolation pour nous, donc $ watch voit une chaîne sans {{}}.) Pour qu'il soit plus facile de se rappeler laquelle utiliser quand, je suggère d'utiliser Observer également pour ce cas.

Pour aider à tester tout cela, j'ai écrit un Plunker qui définit deux directives. Un ( d1) ne crée pas de nouvelle étendue, l'autre ( d2) crée une étendue isolée. Chaque directive a les six mêmes attributs. Chaque attribut est à la fois $ observé et $ surveillé.

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

Regardez le journal de la console pour voir les différences entre $ observ et $ watch dans la fonction de liaison. Cliquez ensuite sur le lien et voyez quels $ observes et $ watch sont déclenchés par les changements de propriétés effectués par le gestionnaire de clic.

Notez que lorsque la fonction de lien s'exécute, tous les attributs qui contiennent des {{}} ne sont pas encore évalués (donc si vous essayez d'examiner les attributs, vous obtiendrez undefined). La seule façon de voir les valeurs interpolées est d'utiliser $ observ (ou $ watch si vous utilisez une portée isolée avec '@'). Par conséquent, l'obtention des valeurs de ces attributs est une opération asynchrone . (Et c'est pourquoi nous avons besoin des fonctions $ observe et $ montre.)

Parfois, vous n'avez pas besoin de $ observe ou $ watch. Par exemple, si votre attribut contient un numéro ou un booléen (pas une chaîne), juste une fois évaluer: attr1="22", puis, disons, votre fonction de liaison: var count = scope.$eval(attrs.attr1). Si c'est juste une chaîne constante - attr1="my string"- alors utilisez simplement attrs.attr1dans votre directive (pas besoin de $ eval ()).

Voir aussi le message du groupe Google de Vojta sur les expressions $ watch.

Mark Rajcok
la source
13
Grande explication! +1
PrimosK
4
Très bonne réponse! Avez-vous une idée pourquoi ng-src/ng-hrefutiliser attr.$observeau lieu de cela scope.$watch?
okm
4
+1 Pour le pape AngularJS! Chaque fois que je recherche dans Stack des informations sur mon dernier problème angulaire, je finis inévitablement par lire la réponse acceptée de @MarkRajcok.
GFoley83
1
Merci pour un excellent article. scope. $ eval (item) est vraiment utile. Si item est une chaîne json, il se convertit en objet json.
bnguyen82
5
@tamakisquare, ils sont interchangeables lors de l'utilisation de la @syntaxe. Je crois qu'il n'y a pas de différence de performances (mais je n'ai pas regardé le code source réel).
Mark Rajcok
25

Si je comprends bien votre question, vous demandez quelle est la différence si vous enregistrez le rappel de l'auditeur avec $watchou si vous le faites avec $observe.

Le rappel enregistré avec $watchest déclenché lors de $digestson exécution.

Le rappel enregistré avec $observeest appelé lorsque la valeur change des attributs qui contiennent une interpolation (par exemple attr="{{notJetInterpolated}}").


Dans la directive Inside, vous pouvez utiliser les deux de manière très similaire:

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

ou

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });
PrimosK
la source
3
En fait, puisque chaque changement est reflété en $digestphase, il est sûr de supposer que le $observerappel sera appelé $digest. Et le $watchrappel sera également appelé $digestmais chaque fois que la valeur est modifiée. Je pense qu'ils font exactement le même travail: "regardez l'expression, rappelez la valeur change". La différence entre les mots clés est peut-être simplement du sucre syntaxique pour ne pas dérouter le développeur.
Umur Kontacı
1
@fastreload, je suis totalement d'accord avec votre commentaire .. Joliment écrit!
PrimosK
@fastreload ... Merci pour la merveilleuse explication. Si j'ai bien compris, les observateurs sont pour les expressions angulaires. Ai-je raison?
Abilash
@PrimosK: vous ajouter pour mon commentaire précédent.
Abilash
2
Les observateurs @Abilash sont pour regarder les attributs dom, pas seulement les expressions. Donc, si vous modifiez la valeur d'attribut par vous-même, cela se reflétera dans le prochain cycle de résumé.
Umur Kontacı
1

Je pense que c'est assez évident:

  • $ observe est utilisé pour lier la fonction des directives.
  • $ watch est utilisé sur la portée pour surveiller tout changement dans ses valeurs.

Gardez à l'esprit : à la fois la fonction a deux arguments,

$observe/$watch(value : string, callback : function);
  • valeur : est toujours une référence de chaîne à l'élément surveillé (le nom de la variable d'une étendue ou le nom de l'attribut de la directive à surveiller)
  • callback : la fonction à exécuter du formulairefunction (oldValue, newValue)

J'ai fait un plunker, donc vous pouvez réellement comprendre à la fois leur utilisation. J'ai utilisé l'analogie Chameleon pour le rendre plus facile à imaginer.

vdegenne
la source
2
Il est assez évident de ses utilisations. Mais pourquoi était la question. Mark l'a résumé magnifiquement.
Abilash
3
Je pense que les paramètres pourraient être changés - il semble passer newValue, puis oldValue à attrs. $ Observe (). . .
blaster
0

Pourquoi $ observe est-il différent de $ watch?

Le watchExpression est évalué et comparé à la valeur précédente à chaque cycle digest (), s'il y a un changement dans la valeur watchExpression, la fonction watch est appelée.

$ observe est spécifique à la recherche de valeurs interpolées. Si la valeur d'attribut d'une directive est interpolée, par exemple dir-attr="{{ scopeVar }}", la fonction observe ne sera appelée que lorsque la valeur interpolée est définie (et donc lorsque $ digest a déjà déterminé des mises à jour doivent être effectuées). Fondamentalement, il existe déjà un observateur pour l'interpolation, et la fonction $ observe se superpose à cela.

Voir $ observe & $ set dans compile.js

Niko
la source