AngularJS: Diferença entre os methods $ observe e $ watch

Eu sei que tanto Watchers quanto Observers são calculados assim que algo no $scope muda no AngularJS. Mas não conseguia entender o que exatamente é a diferença entre os dois.

Meu entendimento inicial é que Observers são computados para expressões angulares que são condições no lado HTML onde os Watchers executados quando a function $scope.$watch() é executada. Eu estou pensando corretamente?

$ observe () é um método no object Attributes e, como tal, só pode ser usado para observar / observar a mudança de valor de um atributo DOM. Só é usado / chamado dentro de diretivas. Use $ observe quando você precisar observar / observar um atributo DOM que contenha interpolação (ou seja, {{}}).
Por exemplo, attr1="Name: {{name}}" , então em uma diretiva: attrs.$observe('attr1', ...) .
(Se você tentar o scope.$watch(attrs.attr1, ...) não funcionará por causa dos {{}} s – você ficará undefined .) Use $ watch para todo o resto.

$ watch () é mais complicado. Pode observar / observar uma “expressão”, onde a expressão pode ser uma function ou uma string. Se a expressão for uma string, ela será $ parse ‘d (isto é, avaliada como uma expressão Angular ) em uma function. (É essa function que é chamada todo ciclo de digitação.) A expressão de seqüência de caracteres não pode conter {{}} ‘s. $ watch é um método no object Scope , então ele pode ser usado / chamado onde quer que você tenha access a um object de escopo, portanto em

  • um controlador – qualquer controlador – um criado via ng-view, ng-controller ou um controlador de diretiva
  • uma function de binding numa directiva, uma vez que esta também tem access a um âmbito

Como strings são avaliadas como expressões Angulares, $ watch é freqüentemente usado quando você deseja observar / observar uma propriedade de modelo / escopo. Por exemplo, attr1="myModel.some_prop" , então, em um controlador ou function de link: scope.$watch('myModel.some_prop', ...) ou scope.$watch(attrs.attr1, ...) (ou scope.$watch(attrs['attr1'], ...) ).
(Se você tentar attrs.$observe('attr1') você obterá a string myModel.some_prop , que provavelmente não é o que você deseja.)

Como discutido nos comentários sobre a resposta da @PrimosK, todos os $ observes e $ watches são verificados em cada ciclo de digitação.

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

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 é $ observado e $ monitorado.

 

Observe o log do console para ver as diferenças entre $ observe e $ watch na function 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 function de link é executada, quaisquer atributos que contenham {{}} não são avaliados ainda (portanto, se você tentar examinar os atributos, ficará 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 avalie-o uma vez: attr1="22" , depois, digamos, em sua function de vinculação: var count = scope.$eval(attrs.attr1) . Se for apenas uma string constante – attr1="my string" – então apenas use attrs.attr1 em sua diretiva (sem necessidade de $ eval ()).

Veja também o post do grupo do gojta sobre expressões de $ watch.

Se eu entendi bem a sua pergunta, você está perguntando qual é a diferença se você registrar o retorno do ouvinte com $watch ou se você fizer isso com $observe .

O callback registado com $watch é acionado quando o $digest é executado.

Callback registrado com $observe são chamados quando alterações de valor de atributos que contêm interpolação (por exemplo, attr="{{notJetInterpolated}}" ).


Dentro da diretiva você pode usar os dois de maneira muito similar:

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

ou

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

Eu acho que isso é bem óbvio:

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

Tenha em mente : tanto a function tem dois argumentos,

 $observe/$watch(value : string, callback : function); 
  • valor : é sempre uma referência de string para o elemento observado (o nome da variável de um escopo ou o nome do atributo da diretiva a ser observado)
  • retorno de chamada : a function a ser executada da function (oldValue, newValue) formulário function (oldValue, newValue)

Eu fiz um plunker , então você pode realmente entender a utilização deles. Eu usei a analogia do Chameleon para tornar mais fácil a imagem.

Por que $ é diferente de $ watch?

O watchExpression é avaliado e comparado com o valor anterior em cada ciclo digest (), se houver uma alteração no valor watchExpression, a function watch é chamada.

$ observe é específico para observar valores interpolados. Se o valor de atributo de uma diretiva é interpolado, por exemplo, dir-attr="{{ scopeVar }}" , a function observe só será chamada quando o valor interpolado for definido (e, portanto, quando $ digest já tiver determinado que as atualizações precisam ser feitas). Basicamente, já existe um observador para a interpolação, e a function $ observe pega essa function.

Veja $ observe & $ definido em compile.js