Como eu uso $ scope, $ watch e $ scope. $ Apply em AngularJS?

Eu não entendo como usar $scope.$watch e $scope.$apply . A documentação oficial não é útil.

O que não entendo especificamente:

  • Eles estão conectados ao DOM?
  • Como posso atualizar as alterações do DOM no modelo?
  • Qual é o ponto de conexão entre eles?

Eu tentei este tutorial , mas é preciso ter o entendimento de $watch e $apply como certo.

O que $apply e $watch fazem e como usá-los adequadamente?

Você precisa estar ciente de como o AngularJS trabalha para entendê-lo.

Ciclo Digest e $ scope

Primeiro e mais importante, o AngularJS define um conceito de um chamado ciclo de digestão . Esse ciclo pode ser considerado como um loop, durante o qual o AngularJS verifica se há alguma alteração em todas as variables observadas por todos os $scope . Portanto, se você tiver $scope.myVar definido em seu controlador e essa variável tiver sido marcada para ser observada , você estará implicitamente informando ao AngularJS para monitorar as alterações em myVar em cada iteração do loop.

Uma questão natural de acompanhamento seria: Está tudo anexado ao $scope sendo assistido? Felizmente não. Se você observasse as alterações em todos os objects em seu $scope , rapidamente um loop digest levaria muito tempo para ser avaliado e você rapidamente se depararia com problemas de desempenho. É por isso que a equipe do AngularJS nos deu duas maneiras de declarar algumas variables ​​do $scope como sendo observadas (leia abaixo).

$ watch ajuda a ouvir mudanças no escopo do $

Existem duas maneiras de declarar uma variável $scope como sendo vigiada.

  1. Ao usá-lo no seu modelo por meio da expressão {{myVar}}
  2. Adicionando-o manualmente através do serviço $watch

Anúncio 1) Este é o cenário mais comum e tenho certeza de que você já o viu antes, mas não sabia que isso criou um relógio em segundo plano. Sim, tinha! O uso de diretivas AngularJS (como ng-repeat ) também pode criar relógios implícitos.

Anúncio 2) É assim que você cria seus próprios relógios . $watch serviço $watch ajuda você a executar algum código quando algum valor anexado ao $scope for alterado. É raramente usado, mas às vezes é útil. Por exemplo, se você quiser executar algum código toda vez que ‘myVar’ for alterado, você poderá fazer o seguinte:

 function MyController($scope) { $scope.myVar = 1; $scope.$watch('myVar', function() { alert('hey, myVar has changed!'); }); $scope.buttonClicked = function() { $scope.myVar = 2; // This will trigger $watch expression to kick in }; } 

$ apply permite integrar mudanças com o ciclo digest

Você pode pensar na function $apply partir de um mecanismo de integração . Você vê, cada vez que você altera alguma variável observada anexada diretamente ao object $scope , o AngularJS saberá que a mudança aconteceu. Isso porque o AngularJS já sabia monitorar essas mudanças. Então, se isso acontecer no código gerenciado pela estrutura, o ciclo de digitação continuará.

No entanto, às vezes você deseja alterar algum valor fora do mundo AngularJS e ver as alterações se propagarem normalmente. Considere isso – você tem um valor $scope.myVar que será modificado dentro de um manipulador $.ajax() do jQuery. Isso vai acontecer em algum momento no futuro. O AngularJS não pode esperar que isso aconteça, já que ele não foi instruído a esperar no jQuery.

Para resolver isso, $apply foi introduzido. Ele permite que você inicie o ciclo de digestão explicitamente. No entanto, você deve usar isso apenas para migrar alguns dados para o AngularJS (integração com outras estruturas), mas nunca use esse método combinado com o código AngularJS regular, pois o AngularJS emitirá um erro.

Como tudo isso está relacionado ao DOM?

Bem, você deve realmente seguir o tutorial novamente, agora que você sabe tudo isso. O ciclo de digestão garantirá que a interface do usuário e o código JavaScript fiquem sincronizados, avaliando todos os observadores anexados a todos os $scope s, desde que nada mude. Se não houver mais mudanças no loop digest, então é considerado terminado.

Você pode append objects ao object $scope explicitamente no Controlador ou declarando-os no formato {{expression}} diretamente na visualização.

Espero que isso ajude a esclarecer alguns conhecimentos básicos sobre tudo isso.

Leituras adicionais:

  • Faça seu próprio AngularJS, parte 1: escopos e resumo

No AngularJS, atualizamos nossos modelos e nossas visualizações / modelos atualizam o DOM “automaticamente” (por meio de diretivas internas ou personalizadas).

$ apply e $ watch, ambos sendo methods de escopo, não estão relacionados ao DOM.

A página de conceitos (seção “Runtime”) tem uma boa explicação do $ digest loop, $ apply, da fila $ evalAsync e da lista $ watch. Aqui está a foto que acompanha o texto:

$ digest loop

Qualquer que seja o código que tenha access a um escopo – normalmente os controladores e diretivas (suas funções de link e / ou seus controladores) – podem configurar um ” watchExpression ” que o AngularJS avaliará em relação a esse escopo. Essa avaliação acontece sempre que o AngularJS entra em seu loop $ digest (em particular, o loop “$ watch list”). Você pode observar as propriedades individuais do escopo, definir uma function para observar duas propriedades juntas, observar o tamanho de uma matriz etc.

Quando as coisas acontecem “dentro do AngularJS” – por exemplo, você digita em uma checkbox de texto que tem binding de dados bidirecional AngularJS habilitada (isto é, usa ng-model), um retorno de chamada $ http triggers, etc. – $ apply já foi chamado está dentro do retângulo “AngularJS” na figura acima. Todos os watchExpressions serão avaliados (possivelmente mais de uma vez – até que nenhuma outra alteração seja detectada).

Quando as coisas acontecem “fora do AngularJS” – por exemplo, você usou bind () em uma diretiva e, em seguida, esse evento é triggersdo, resultando em seu retorno de chamada sendo chamado ou alguns disparos de retorno de chamada registrados do jQuery – ainda estamos no retângulo “Nativo”. Se o código de retorno de chamada modificar qualquer coisa que qualquer $ watch esteja assistindo, ligue $ apply para entrar no retângulo AngularJS, fazendo com que o loop $ digest seja executado e, portanto, o AngularJS notará a mudança e fará sua mágica.

Este blog foi coberto todos os exemplos de criação e explicações compreensíveis.

As funções de $scope AngularJS $scope $watch(), $digest() e $apply() são algumas das funções centrais do AngularJS. Entender $watch() , $digest() e $apply() é essencial para entender o AngularJS.

Quando você cria uma binding de dados de algum lugar em sua visualização para uma variável no object $ scope, o AngularJS cria um “watch” internamente. Um relógio significa que o AngularJS observa mudanças na variável no $scope object . O framework está “observando” a variável. Os relógios são criados usando a function $scope.$watch() , que abordarei mais adiante neste texto.

Em pontos chave em seu aplicativo, o AngularJS chama a function $scope.$digest() . Esta function repete todos os relógios e verifica se alguma das variables ​​observadas foi alterada. Se uma variável observada foi alterada, uma function de ouvinte correspondente é chamada. A function listener faz o trabalho necessário, por exemplo, alterando um texto HTML para refletir o novo valor da variável observada. Assim, a function $digest() é o que aciona a binding de dados para atualizar.

Na maioria das vezes, o AngularJS chamará as funções $ scope, $ watch () e $scope.$digest() para você, mas em algumas situações você pode ter que chamá-las você mesmo. Por isso, é muito bom saber como eles funcionam.

A function $scope.$apply() é usada para executar algum código e, em seguida, chama $scope.$digest() depois disso, então todos os relógios são verificados e as funções correspondentes do watch watchener são chamadas. A function $apply() é útil ao integrar o AngularJS com outro código.

Entrarei em mais detalhes sobre as funções $watch(), $digest() e $apply() no restante deste texto.

$ watch ()

A function $scope.watch() cria um relógio de alguma variável. Quando você registra um relógio, você passa duas funções como parâmetros para a function $watch() :

  • Uma function de valor
  • Uma function de ouvinte

Aqui está um exemplo:

 $scope.$watch(function() {}, function() {} ); 

A primeira function é a function value e a segunda function é a function listener.

A function valor deve retornar o valor que está sendo observado. O AngularJS pode então verificar o valor retornado em relação ao valor que a function watch retornou na última vez. Dessa forma, o AngularJS pode determinar se o valor foi alterado. Aqui está um exemplo:

 $scope.$watch(function(scope) { return scope.data.myVar }, function() {} ); 

Esta function valule de exemplo retorna a variável $scope scope.data.myVar . Se o valor dessa variável for alterado, um valor diferente será retornado e o AngularJS chamará a function listener.

Observe como a function de valor leva o escopo como parâmetro (sem o $ no nome). Através deste parâmetro, a function value pode acessar o $scope e suas variables. A function value também pode observar as variables ​​globais se você precisar disso, mas na maioria das vezes você observará uma variável $scope .

A function de ouvinte deve fazer o que for necessário se o valor for alterado. Talvez você precise alterar o conteúdo de outra variável ou definir o conteúdo de um elemento HTML ou algo assim. Aqui está um exemplo:

 $scope.$watch(function(scope) { return scope.data.myVar }, function(newValue, oldValue) { document.getElementById("").innerHTML = "" + newValue + ""; } ); 

Este exemplo define o HTML interno de um elemento HTML para o novo valor da variável, incorporado no elemento b, que torna o valor em negrito. É claro que você poderia ter feito isso usando o código {{ data.myVar } , mas isso é apenas um exemplo do que você pode fazer dentro da function de ouvinte.

$ digest ()

A function $scope.$digest() itera através de todos os relógios no $scope object e seus objects child $ scope (se houver algum). Quando $digest() itera sobre os relógios, ele chama a function de valor para cada relógio. Se o valor retornado pela function value for diferente do valor retornado na última vez em que foi chamado, a function listener desse watch será chamada.

A function $digest() é chamada sempre que o AngularJS achar necessário. Por exemplo, depois que um manipulador de clique de botão foi executado, ou depois que uma chamada de AJAX retorna (depois que a function de retorno de chamada done () / fail () foi executada).

Você pode encontrar alguns casos em que o AngularJS não chama a function $digest() para você. Você geralmente detectará isso observando que as ligações de dados não atualizam os valores exibidos. Nesse caso, chame $scope.$digest() e isso deve funcionar. Ou talvez você possa usar o $scope.$apply() que explicarei na próxima seção.

$ apply ()

A function $scope.$apply() assume uma function como parâmetro que é executada, e após esse $scope.$digest() é chamado internamente. Isso torna mais fácil para você garantir que todos os relógios sejam verificados e, assim, todas as ligações de dados sejam atualizadas. Aqui está um exemplo de $apply() :

 $scope.$apply(function() { $scope.data.myVar = "Another value"; }); 

A function passada para a function $apply() como parâmetro alterará o valor de $scope.data.myVar . Quando a function sair, o AngularJS chamará a function $scope.$digest() para que todos os relógios sejam verificados quanto a alterações nos valores observados.

Exemplo

Para ilustrar como funciona $watch() , $digest( ) e $apply() , veja este exemplo:

 
{{data.time}}

Este exemplo liga a variável $scope.data.time a uma diretiva de interpolação que mescla o valor da variável na página HTML. Essa binding cria um relógio internamente na $scope.data.time variable .

O exemplo também contém dois botões. O primeiro botão tem um ouvinte de ng-click anexado a ele. Quando esse botão é clicado, a function $scope.updateTime() é chamada e, depois disso, o AngularJS chama $scope.$digest() para que as ligações de dados sejam atualizadas.

O segundo botão recebe um ouvinte de evento JavaScript padrão anexado a ele de dentro da function do controlador. Quando o segundo botão é clicado, essa function do ouvinte é executada. Como você pode ver, as funções do ouvinte para os dois botões são quase iguais, mas quando a function listener do segundo botão é chamada, a binding de dados não é atualizada. Isso porque o $scope.$digest() não é chamado depois que o ouvinte de events do segundo botão é executado. Portanto, se você clicar no segundo botão, a hora será atualizada na variável $scope.data.time , mas a nova hora nunca será exibida.

Para consertar isso, podemos adicionar uma chamada $scope.$digest() para a última linha do ouvinte de events do botão, assim:

 document.getElementById("updateTimeButton") .addEventListener('click', function() { console.log("update time clicked"); $scope.data.time = new Date(); $scope.$digest(); }); 

Em vez de chamar $digest() dentro da function de listener de botão, você também poderia ter usado a function $apply() assim:

 document.getElementById("updateTimeButton") .addEventListener('click', function() { $scope.$apply(function() { console.log("update time clicked"); $scope.data.time = new Date(); }); }); 

Observe como a function $scope.$apply() é chamada dentro do ouvinte de events do botão e como a atualização da variável $scope.data.time é executada dentro da function passada como parâmetro para a function $apply() . Quando a chamada da function $apply() termina chamadas do AngularJS $digest() internamente, todas as ligações de dados são atualizadas.

O AngularJS estende esse loop de events , criando algo chamado AngularJS context .

$ watch ()

Toda vez que você vincula algo na interface do usuário, você insere um $watch em uma lista de $watch .

 User:  Password:  

Aqui temos $scope.user , que está ligado à primeira input, e temos $scope.pass , que está ligado ao segundo. Fazendo isso, adicionamos dois $watch watches à lista de $watch .

Quando nosso modelo é carregado, o AKA na fase de vinculação, o compilador procurará todas as diretivas e criará todos os $watch watches necessários.

O AngularJS oferece $watch , $watchcollection e $watch(true) . Abaixo está um diagrama puro explicando todos os três dados dos observadores em profundidade .

Digite a descrição da imagem aqui

 angular.module('MY_APP', []).controller('MyCtrl', MyCtrl) function MyCtrl($scope,$timeout) { $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}]; $scope.$watch("users", function() { console.log("**** reference checkers $watch ****") }); $scope.$watchCollection("users", function() { console.log("**** Collection checkers $watchCollection ****") }); $scope.$watch("users", function() { console.log("**** equality checkers with $watch(true) ****") }, true); $timeout(function(){ console.log("Triggers All ") $scope.users = []; $scope.$digest(); console.log("Triggers $watchCollection and $watch(true)") $scope.users.push({ name: 'Thalaivar'}); $scope.$digest(); console.log("Triggers $watch(true)") $scope.users[0].name = 'Superstar'; $scope.$digest(); }); } 

http://jsfiddle.net/2Lyn0Lkb/

$digest loop

Quando o navegador recebe um evento que pode ser gerenciado pelo contexto AngularJS, o loop $digest será acionado. Esse loop é feito de dois loops menores. Um processa a fila $evalAsync e o outro processa a $watch list . O $digest irá percorrer a lista de $watch que temos

 app.controller('MainCtrl', function() { $scope.name = "vinoth"; $scope.changeFoo = function() { $scope.name = "Thalaivar"; } }); {{ name }}  

Aqui temos apenas um $watch porque o ng-click não cria nenhum relógio.

Nós pressionamos o botão.

  1. O navegador recebe um evento que entrará no contexto do AngularJS
  2. O loop $digest irá rodar e perguntará a cada $ watch por mudanças.
  3. Como o $watch que estava observando as alterações no $ scope.name reporta uma alteração, isso forçará outro loop $digest .
  4. O novo loop não informa nada.
  5. O navegador recupera o controle e atualizará o DOM refletindo o novo valor de $ scope.name
  6. O importante aqui é que CADA evento que entra no contexto do AngularJS executará um loop $digest . Isso significa que toda vez que escrevermos uma carta em uma input, o loop será executado checando cada $watch nesta página.

$ apply ()

Se você chamar $apply quando um evento for triggersdo, ele passará pelo contexto angular, mas se você não o chamar, ele será executado fora dele. É tão fácil quanto isso. $apply chamará o loop $digest() internamente e iterará sobre todos os relógios para garantir que o DOM seja atualizado com o valor recém-atualizado.

O método $apply() acionará observadores em toda a cadeia de $scope , enquanto o método $digest() acionará apenas observadores no $scope atual e seus children . Quando nenhum dos objects de $scope superiores precisa saber sobre as mudanças locais, você pode usar $digest() .

$watchGroup e $watchCollection também. Especificamente, $watchGroup é realmente útil se você quiser chamar uma function para atualizar um object que tenha múltiplas propriedades em uma visão que não seja object dom, por exemplo, outra visão em canvas, webGL ou requisição de servidor. Aqui, o link da documentação.

Encontrei vídeos muito detalhados que abrangem os ciclos $watch , $apply , $digest e digest em:

  • AngularJS – Entendendo o Watcher, $ watch, $ watchGroup, $ watchCollection, ng-change

  • AngularJS – Entendendo o ciclo digestivo (digerir fase ou digerir processo ou digerir loop)

  • AngularJS Tutorial – Noções básicas sobre $ apply e $ digest (em profundidade)

A seguir estão alguns slides usados ​​nesses vídeos para explicar os conceitos (apenas no caso, se os links acima forem removidos / não funcionarem).

Digite a descrição da imagem aqui

Na imagem acima, “$ scope.c” não está sendo visto, pois não é usado em nenhuma das ligações de dados (na marcação). Os outros dois ( $scope.a e $scope.b ) serão assistidos.

Digite a descrição da imagem aqui

A partir da imagem acima: Com base no respectivo evento do navegador, o AngularJS captura o evento, executa o ciclo de digitação (percorre todos os relógios em busca de alterações), executa funções de observação e atualiza o DOM. Se não forem events do navegador, o ciclo de digitação pode ser acionado manualmente usando $apply ou $digest .

Mais sobre $apply e $digest :

Digite a descrição da imagem aqui

Basta terminar de ler todos os acima, chato e sonolento (desculpe, mas é verdade). Muito técnico, profundo, detalhado e seco. Por que estou escrevendo? Como o AngularJS é enorme, muitos conceitos interconectados podem tornar alguém maluco. Muitas vezes me perguntei: não sou inteligente o suficiente para entendê-las? Não! É porque tão poucos conseguem explicar a tecnologia em uma linguagem for dummie sem todas as terminologias! Ok, deixe-me tentar:

1) Eles são todos coisas dirigidas por events. (Eu ouço o riso, mas continuo a ler)

Se você não sabe o que é controlado por evento Então pense em colocar um botão na página, conecte-o com uma function usando o “clique”, esperando que os usuários cliquem nele para acionar as ações que você plantou dentro do function. Ou pense em “gatilho” do SQL Server / Oracle.

2) $ watch é “on-click”.

O que há de especial é que são necessárias duas funções como parâmetros, a primeira delas fornece o valor do evento, a segunda leva o valor em consideração …

3) $ digest é o chefe que verifica incansavelmente , mas um bom chefe.

4) $ apply dá-lhe o caminho quando você quer fazê-lo manualmente , como uma prova de falha (no caso de no-clique não chutar, você forçá-lo a correr.)

Agora vamos tornar isso visual. Imagine isso para tornar ainda mais fácil entender a ideia:

Em um restaurante,

– Os garçons devem receber pedidos de clientes, isso é

 $watch( function(){return orders;}, function(){Kitchen make it;} ); 

– GERENTE correndo para se certificar de que todos os garçons estão acordados, responsivos a qualquer sinal de mudanças dos clientes. Isso é $digest()

– PROPRIETÁRIO tem o poder final para conduzir todos a pedido, isto é $apply()