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




scope watch scope var (5)

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 para o 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?


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 eventos. (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 função usando o "clique", esperando que os usuários cliquem nele para acionar as ações que você plantou dentro do função. 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()


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

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).

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.

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 eventos do navegador, o ciclo de digitação pode ser acionado manualmente usando $apply ou $digest .

Mais sobre $apply e $digest :


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 métodos de escopo, não estão relacionados ao DOM.

A página de Concepts (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:

Qualquer que seja o código que tenha acesso a um escopo - normalmente controladores e diretivas (suas funções de link e / ou seus controladores) - podem configurar uma " 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 função 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 caixa de texto que tem ligação de dados bidirecional AngularJS habilitada (isto é, usa ng-model), um retorno de chamada $ http dispara, 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 é disparado, 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.


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

$ watch ()

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

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

Aqui temos $scope.user , que está ligado à primeira entrada, 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 .

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 }}
<button ng-click="changeFoo()">Change the name</button>

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 entrada, o loop será executado checando cada $watch nesta página.

$ apply ()

Se você chamar $apply quando um evento for disparado, 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 objetos de $scope superiores precisa saber sobre as mudanças locais, você pode usar $digest() .


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 ligação de dados de algum lugar em sua visualização para uma variável no objeto $ 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 função $scope.$watch() , que abordarei mais adiante neste texto.

Em pontos chave em seu aplicativo, o AngularJS chama a função $scope.$digest() . Esta função repete todos os relógios e verifica se alguma das variáveis ​​observadas foi alterada. Se uma variável observada foi alterada, uma função de ouvinte correspondente é chamada. A função listener faz o trabalho necessário, por exemplo, alterando um texto HTML para refletir o novo valor da variável observada. Assim, a função $digest() é o que aciona a ligação 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 função $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 função $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 função $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 função $watch() :

  • Uma função de valor
  • Uma função de ouvinte

Aqui está um exemplo:

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

A primeira função é a função value e a segunda função é a função listener.

A função 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 função 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 função 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 função listener.

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

A função 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 função de ouvinte.

$ digest ()

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

A função $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 função de retorno de chamada done () / fail () foi executada).

Você pode encontrar alguns casos em que o AngularJS não chama a função $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 função $scope.$apply() assume uma função 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 função passada para a função $apply() como parâmetro alterará o valor de $scope.data.myVar . Quando a função sair, o AngularJS chamará a função $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:

<div ng-controller="myController">
    {{data.time}}

    <br/>
    <button ng-click="updateTime()">update time - ng-click</button>
    <button id="updateTimeButton"  >update time</button>
</div>


<script>
    var module       = angular.module("myapp", []);
    var myController1 = module.controller("myController", function($scope) {

        $scope.data = { time : new Date() };

        $scope.updateTime = function() {
            $scope.data.time = new Date();
        }

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

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 ligação 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 função $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 função do controlador. Quando o segundo botão é clicado, essa função do ouvinte é executada. Como você pode ver, as funções do ouvinte para os dois botões são quase iguais, mas quando a função listener do segundo botão é chamada, a ligação de dados não é atualizada. Isso porque o $scope.$digest() não é chamado depois que o ouvinte de eventos 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 eventos 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 função de listener de botão, você também poderia ter usado a função $apply() assim:

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

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





angularjs-scope