AngularJS: Diferença entre os métodos $ observe e $ watch

378

Eu sei que ambos Watcherse Observerssão computados assim que algo $scopemuda no AngularJS. Mas não conseguia entender qual é exatamente a diferença entre os dois.

Meu entendimento inicial é que Observerssão calculados para expressões angulares que são condições no lado HTML, onde Watchersexecutadas quando a $scope.$watch()função é executada. Estou pensando corretamente?

Abilash
fonte
11
Sua edição não é útil e um pouco antagônica. Por favor, seja atencioso com os outros que vêm aqui para obter ajuda real.
Smalone 17/08/16
@smalone mudou. Obrigado e desculpe!
Abilash
👍 Não se preocupe. Obrigado pela correção.
Smalone 15/09/16

Respostas:

608

$ observe () é um método noobjeto Atributos e, como tal, só pode ser usado para observar / observar a alteração de valor de um atributo DOM. Só é usado / chamado dentro de diretivas. Use $ observe quando precisar observar / observar um atributo DOM que contenha interpolação (ou seja, {{}} ').
Por exemplo,attr1="Name: {{name}}"e depois numa directiva:attrs.$observe('attr1', ...).
(Se você tentarscope.$watch(attrs.attr1, ...), não funcionará por causa dos {{}} s - você receberáundefined.) Use $ watch para todo o resto.

$ watch () é mais complicado. Ele pode observar / observar uma "expressão", onde a expressão pode ser uma função ou uma string. Se a expressão for uma string, é $ parse 'd (isto é, avaliado como uma expressão Angular ) em uma função. (É essa função que é chamada todo ciclo de digestão.) A expressão da string não pode conter {{}} 's. $ watch é um método noobjeto Scope , portanto, pode ser usado / chamado sempre que você tiver acesso a um objeto de escopo, portanto, em

  • um controlador - qualquer controlador - um criado via ng-view, ng-controller ou um controlador de diretiva
  • uma função de vinculação em uma diretiva, pois ela também tem acesso a um escopo

Como as seqüências de caracteres são avaliadas como expressões angulares, $ watch geralmente é usado quando você deseja observar / observar uma propriedade de modelo / escopo. Por exemplo, attr1="myModel.some_prop"em uma função de controlador ou link: scope.$watch('myModel.some_prop', ...)ou scope.$watch(attrs.attr1, ...)(ou scope.$watch(attrs['attr1'], ...)).
(Se você tentar, attrs.$observe('attr1')obterá a string myModel.some_prop, o que provavelmente não é o que você deseja.)

Conforme discutido nos comentários da resposta do @ PrimosK, todos os $ observes e $ watches são verificados a cada ciclo de digestão .

Diretivas com escopos isolados são mais complicadas. Se a sintaxe '@' for usada, você pode $ observar ou $ assistir um atributo DOM que contenha interpolação (ou seja, {{}} '). (O motivo pelo qual ele trabalha com $ watch é porque a sintaxe '@' faz a interpolação para nós, portanto, $ watch vê uma string sem {{}} '.) Para facilitar a lembrança de qual usar, sugiro usar $ observe também para este caso.

Para ajudar a testar tudo isso, escrevi um Plunker que define duas diretivas. Um ( d1) não cria um novo escopo, o outro ( d2) cria um escopo isolado. Cada diretiva tem os mesmos seis atributos. Cada atributo é $ observe'd e $ watch'ed.

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

Veja o log do console para ver as diferenças entre $ observe e $ watch na função de vinculação. Em seguida, clique no link e veja quais $ observa e $ relógios são acionados pelas alterações de propriedade feitas pelo manipulador de cliques.

Observe que quando a função de link é executada, quaisquer atributos que contenham {{}} ainda não são avaliados (portanto, se você tentar examinar os atributos, obterá undefined). A única maneira de ver os valores interpolados é usar $ observe (ou $ watch se estiver usando um escopo isolado com '@'). Portanto, obter os valores desses atributos é uma operação assíncrona . (E é por isso que precisamos das funções $ observe e $ watch.)

Às vezes você não precisa de $ observe ou $ watch. Por exemplo, se o seu atributo contém um número ou um booleano (não uma string), apenas avaliá-lo uma vez: attr1="22"e, em seguida, digamos, a sua função que relaciona: var count = scope.$eval(attrs.attr1). Se for apenas uma string constante - attr1="my string"- apenas use attrs.attr1na sua diretiva (não é necessário $ eval ()).

Veja também o post do grupo do Google sobre Vojta sobre expressões $ watch.

Mark Rajcok
fonte
13
Ótima explicação! +1
PrimosK 22/02
4
Ótima resposta! Você tem idéia do porquê ng-src/ng-hrefusar em attr.$observevez scope.$watchdisso?
Ok #
4
+1 Para o Papa AngularJS! Toda vez que procuro no Stack por informações sobre meu último problema Angular, inevitavelmente acabo lendo a resposta aceita por @MarkRajcok.
GFoley83
11
Obrigado por um ótimo post. $ eval (item) é realmente útil. Se item for uma string json, ele será convertido em um objeto json.
precisa saber é o seguinte
5
@tamakisquare, eles são intercambiáveis ​​ao usar a @sintaxe. Eu acredito que não há diferença de desempenho (mas eu não olhei o código fonte real).
precisa saber é o seguinte
25

Se entendi bem sua pergunta, você está perguntando qual é a diferença se registrar o retorno de chamada do ouvinte $watchou se o fizer $observe.

O retorno de chamada registerd com $watché acionado quando $digesté executado.

O retorno de $observechamada registrado com é chamado quando alterações de valor de atributos que contêm interpolação (por exemplo attr="{{notJetInterpolated}}").


Diretiva interna, você pode usar os dois de maneira muito semelhante:

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

ou

    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });
PrimosK
fonte
3
Na verdade, como todas as alterações são refletidas na $digestfase, é seguro assumir que o $observeretorno de chamada será chamado $digest. E o $watchretorno de chamada também será chamado, $digestmas sempre que o valor for alterado. Eu acho que eles fazem exatamente o mesmo trabalho: "observe a expressão, ligue para retorno de chamada, o valor muda". A diferença da palavra-chave é possivelmente apenas açúcar sintático para não confundir o desenvolvedor.
Umur Kontacı
11
@astreload, concordo totalmente com o seu comentário .. Muito bem escrito!
PrimosK
@fastreload ... Obrigado pela maravilhosa explicação. Se bem entendi, os observadores são para expressões angulares. Estou certo?
Abilash
@PrimosK: adicionando você ao meu comentário anterior.
Abilash
2
Os observadores do @Abilash são para assistir a atributos do Dom, não apenas expressões. Portanto, se você alterar o valor do atributo por si mesmo, ele será refletido no próximo ciclo de resumo.
Umur Kontacı
1

Eu acho que isso é bastante óbvio:

  • $ observe é usado na função de vinculação de diretivas.
  • $ watch é usado no escopo para observar qualquer alteração em seus valores.

Lembre-se : a função possui dois argumentos,

$observe/$watch(value : string, callback : function);
  • value : é sempre uma referência de string ao elemento monitorado (o nome da variável de um escopo ou o nome do atributo da diretiva a ser monitorado)
  • retorno de chamada : a função a ser executada no formuláriofunction (oldValue, newValue)

Eu fiz um plunker, para que você possa realmente entender sobre a utilização deles. Eu usei a analogia do Chameleon para facilitar a imagem.

vdegenne
fonte
2
É bastante óbvio sobre seus usos. Mas por que estava a pergunta? Mark resumiu lindamente.
Abilash
3
Eu acho que os parâmetros podem ser alterados - parece passar newValue, então oldValue para attrs. $ Observe (). . .
Blaster #
0

Por que $ observe é diferente de $ watch?

A watchExpression é avaliada e comparada com o valor anterior a cada ciclo digest (). Se houver uma alteração no valor watchExpression, a função watch será chamada.

$ observe é específico para observar valores interpolados. Se o valor do atributo de uma diretiva for interpolado, por exemplo dir-attr="{{ scopeVar }}", a função de observação será chamada apenas quando o valor interpolado estiver definido (e, portanto, quando $ digest já determinar que é necessário fazer atualizações). Basicamente, já existe um observador para a interpolação, e a função $ observe pega isso.

Veja $ observe e $ set em compile.js

Niko
fonte