Como a ligação de dados funciona no AngularJS?

1957

Como a ligação de dados funciona na AngularJSestrutura?

Não encontrei detalhes técnicos em seu site . É mais ou menos claro como ele funciona quando os dados são propagados da visualização para o modelo. Mas como o AngularJS rastreia alterações nas propriedades do modelo sem setters e getters?

Descobri que existem observadores de JavaScript que podem fazer esse trabalho. Mas eles não são suportados no Internet Explorer 6 e Internet Explorer 7 . Então, como o AngularJS sabe que eu mudei, por exemplo, o seguinte e refleti essa alteração em uma exibição?

myobject.myproperty="new value";
Pashec
fonte
10
Esteja ciente de que, desde o angular 1.0.0rc1, você precisa especificar ng-model-instant ( docs-next.angularjs.org/api/… ) para que seu moder seja atualizado constantemente. Caso contrário, ele será atualizado no evento de desfoque.
Sotomajor
8
Aparentemente, o link de Marcello está quebrado, então aqui está novamente: github.com/mhevery/angular.js/blob/master/docs/content/guide/…
riffraff
6
@orian, esse link está ruim. atualizado para (presumo) é o mesmo - docs.angularjs.org/guide/databinding
Kevin Meredith
11
Para aqueles que ainda estão lendo esta pergunta, observe que o Angular 2.0 mudou muito a forma como eles ligam o databinding desde o Angular 1.x para trabalhar com componentes da Web e resolver muitos dos problemas nas respostas abaixo.
Aug

Respostas:

2745

AngularJS lembra o valor e o compara com um valor anterior. Isso é checagem suja básica. Se houver uma alteração no valor, ele dispara o evento de alteração.

O $apply()método, que é o que você chama quando está migrando de um mundo não AngularJS para um mundo AngularJS, é chamado $digest(). Um resumo é simplesmente uma checagem suja e antiga. Ele funciona em todos os navegadores e é totalmente previsível.

Para comparar a verificação suja (AngularJS) x os ouvintes de alteração ( KnockoutJS e Backbone.js ): Embora a verificação suja possa parecer simples e até ineficiente (abordarei isso mais tarde), verifica-se que ela é semanticamente correta o tempo todo, enquanto os ouvintes de mudança têm muitos casos de canto estranhos e precisam de coisas como rastreamento de dependência para torná-lo mais semanticamente correto. O rastreamento de dependência do KnockoutJS é um recurso inteligente para um problema que o AngularJS não possui.

Problemas com ouvintes de mudança:

  • A sintaxe é atroz, pois os navegadores não a suportam nativamente. Sim, existem proxies, mas eles não são semanticamente corretos em todos os casos e, é claro, não há proxies em navegadores antigos. A conclusão é que a verificação suja permite fazer POJO , enquanto o KnockoutJS e o Backbone.js forçam você a herdar de suas classes e acessar seus dados através de acessadores.
  • Mude a coalescência. Suponha que você tenha uma variedade de itens. Digamos que você queira adicionar itens a uma matriz, à medida que você adiciona um loop, a cada vez que você adiciona, você dispara eventos com alterações, que estão renderizando a interface do usuário. Isso é muito ruim para o desempenho. O que você deseja é atualizar a interface do usuário apenas uma vez, no final. Os eventos de mudança são muito detalhados.
  • Os ouvintes de alterações são acionados imediatamente em um setter, o que é um problema, pois o ouvinte de alterações pode alterar ainda mais os dados, o que aciona mais eventos de alteração. Isso é ruim, pois na sua pilha você pode ter vários eventos de alteração acontecendo ao mesmo tempo. Suponha que você tenha duas matrizes que precisam ser mantidas sincronizadas por qualquer motivo. Você pode adicionar apenas a um ou outro, mas cada vez que você adiciona, você dispara um evento de mudança, que agora tem uma visão inconsistente do mundo. Esse é um problema muito semelhante ao bloqueio de encadeamento, que o JavaScript evita, pois cada retorno de chamada é executado exclusivamente e até a conclusão. Os eventos de mudança quebram isso, pois os setters podem ter conseqüências de longo alcance que não são intencionais e não óbvias, o que cria o problema de encadeamento novamente. Acontece que o que você quer fazer é atrasar a execução do ouvinte e garantir,

E o desempenho?

Portanto, pode parecer que somos lentos, pois a verificação suja é ineficiente. É aqui que precisamos olhar para números reais em vez de apenas argumentos teóricos, mas primeiro vamos definir algumas restrições.

Os seres humanos são:

  • Lento - Qualquer coisa acima de 50 ms é imperceptível aos seres humanos e, portanto, pode ser considerado "instantâneo".

  • Limitado - você não pode realmente mostrar mais de 2000 informações a um humano em uma única página. Qualquer coisa além disso é uma interface do usuário realmente ruim, e os humanos não podem processar isso de qualquer maneira.

Portanto, a verdadeira questão é a seguinte: quantas comparações você pode fazer em um navegador em 50 ms? Essa é uma pergunta difícil de responder ao maior número de fatores, mas aqui está um caso de teste: http://jsperf.com/angularjs-digest/6, que cria 10.000 observadores. Em um navegador moderno, isso leva pouco menos de 6 ms. No Internet Explorer 8, são necessários cerca de 40 ms. Como você pode ver, isso não é um problema, mesmo em navegadores lentos atualmente. Há uma ressalva: as comparações precisam ser simples para se ajustarem ao limite de tempo ... Infelizmente, é muito fácil adicionar uma comparação lenta no AngularJS; portanto, é fácil criar aplicativos lentos quando você não sabe o que deseja. estão fazendo. Mas esperamos ter uma resposta, fornecendo um módulo de instrumentação, que mostre quais são as comparações lentas.

Acontece que os videogames e GPUs usam a abordagem de verificação suja, especificamente porque é consistente. Contanto que eles superem a taxa de atualização do monitor (normalmente de 50 a 60 Hz ou a cada 16,6 a 20 ms), qualquer desempenho excedente é um desperdício; portanto, é melhor desenhar mais coisas do que aumentar o FPS.

Misko Hevery
fonte
32
@ Mark - sim, no KO, você apenas adiciona .extend ({throttle: 500}) para aguardar 500 milissegundos após o último evento de mudança antes de agir sobre ele.
Daniel Earwicker
158
Essa resposta inteira é ótima além de "Desde que eles obtenham 50 qps, qualquer desempenho acima disso é um desperdício, já que o olho humano não pode apreciá-lo, então é melhor desenhar mais coisas do que aumentar os qps". Essa declaração está completamente incorreta, dependendo do seu aplicativo. Definitivamente, o olho pode apreciar mais de 50 qps e, como mostram os vários problemas com VR (leia qualquer um dos mais recentes de John Carmack ou Michael Abrash, especialmente a palestra GDC 2013 VR deste último), 50 qps é realmente muito lento. Fora isso, sua resposta é ótima. Eu só não quero que a desinformação se espalhe.
Nate Bundy
10
@DavidRivers nós é uS assim como no utorrent 1μs = 0.000001s
Thorgeir
33
A afirmação poderia ser dita facilmente ao contrário: "A verificação suja é um recurso inteligente para um problema que o nocaute não tem". O ES6 está usando observáveis ​​e o angular está se livrando da verificação suja. O mundo real pegou essa resposta e mostrou que ela era falsa.
cônico
17
"Qualquer coisa acima de 50 ms é imperceptível para humanos" não é verdade. Nos testes, descobrimos que nossos clientes podem distinguir facilmente entre a latência de atualização de 50ms (20fps) e a latência de atualização de 16,6ms (60fps). As cenas que correm na velocidade anterior ficam consistentemente mais pobres em geral "como se sentiu" nas classificações, mesmo quando as pessoas não registravam conscientemente a taxa de quadros.
Crashworks
323

Misko já deu uma excelente descrição de como as ligações de dados funcionam, mas eu gostaria de adicionar minha visão sobre o problema de desempenho à ligação de dados.

Como afirmou Misko, por volta de 2000 ligações é onde você começa a ver problemas, mas você não deve ter mais de 2000 informações em uma página. Isso pode ser verdade, mas nem todas as ligações de dados são visíveis para o usuário. Depois de começar a criar qualquer tipo de widget ou grade de dados com ligação bidirecional, é possível acessar facilmente as ligações com 2000, sem ter um UX ruim.

Considere, por exemplo, uma caixa de combinação na qual você pode digitar texto para filtrar as opções disponíveis. Esse tipo de controle pode ter ~ 150 itens e ainda ser altamente utilizável. Se houver algum recurso extra (por exemplo, uma classe específica na opção atualmente selecionada), você começará a receber 3-5 ligações por opção. Coloque três desses widgets em uma página (por exemplo, um para selecionar um país, o outro para selecionar uma cidade no país em questão e o terceiro para selecionar um hotel) e você já deve ter entre 1000 e 2000 ligações.

Ou considere uma grade de dados em um aplicativo Web corporativo. 50 linhas por página não são irracionais, cada uma delas com 10 a 20 colunas. Se você criar isso com ng-repetições e / ou tiver informações em algumas células que usam algumas ligações, poderá aproximar-se de 2000 ligações apenas com essa grade.

Acho que esse é um problema enorme ao trabalhar com o AngularJS, e a única solução que consegui encontrar até agora é construir widgets sem usar a ligação bidirecional, em vez de usar o ngOnce, cancelando o registro de observadores e truques semelhantes ou construindo diretivas que constroem o DOM com jQuery e manipulação de DOM. Eu sinto que isso derrota o propósito de usar Angular em primeiro lugar.

Eu adoraria ouvir sugestões de outras maneiras de lidar com isso, mas talvez eu deva escrever minha própria pergunta. Eu queria colocar isso em um comentário, mas acabou sendo muito longo para isso ...

TL; DR
A ligação de dados pode causar problemas de desempenho em páginas complexas.

MW.
fonte
26
Sim, eu segundo isso. A principal responsabilidade do nosso aplicativo é exibir conexões entre diferentes entidades. Uma determinada página pode ter 10 seções. Cada seção tem uma tabela. Cada tabela possui de 2 a 5 filtros de digitação antecipada. Cada tabela possui de 2 a 5 colunas, cada uma com 10 linhas. Muito rapidamente nos deparamos com problemas de desempenho e acompanhamos as opções de "truques semelhantes".
Scott Silvi
10
É justo dizer que o Angular não se refere apenas à ligação de dados e alguns aplicativos podem não querer usar esse recurso exatamente pelas razões que outros citaram? Eu acho que a abordagem de DI e modularidade vale muito; ter ligação automática mágica é bom, mas em todas as implementações existentes há compensações de desempenho. O caminho da Angular é indiscutivelmente superior para a maioria dos aplicativos da web CRUD, e as pessoas estão apenas atingindo um muro tentando levá-lo ao extremo. Seria bom ter um método alternativo de escuta de eventos suportado, mas talvez isso seja fundamentalmente complexo demais para uma única estrutura?
Jason Boyd
8
O Angular agora tem uma maneira e ligação de dados vincular uma vez para ajudar com esse problema. Além disso, agora ele possui índices para a sua fonte repetidora, o que permite modificar a lista sem reconstruir o dom para todo o conteúdo.
Gaute Løken
6
@MW. Honestamente, eu pensei que o bind-once estava no centro. Mas parece que não é. É apenas algo que você pode fazer ao escrever suas próprias diretivas, basicamente vinculando coisas sem observá-las. No entanto, há um mod UX para ele: github.com/pasvaz/bindonce
Gaute Løken
9
Um grito do futuro para quem lê este: uma ligação hora é agora uma característica fundamental do v1.3 Angular, leia mais aqui: docs.angularjs.org/guide/expression
Nobita
158

Verificando sujo o $scopeobjeto

Angular mantém um simples arrayobservador nos $scopeobjetos. Se você inspecionar algum, $scopeverá que ele contém um arraychamado $$watchers.

Cada observador é um objectque contém, entre outras coisas

  1. Uma expressão que o observador está monitorando. Pode ser apenas um attributenome ou algo mais complicado.
  2. Um último valor conhecido da expressão. Isso pode ser verificado com relação ao valor atual calculado da expressão. Se os valores diferirem, o inspetor acionará a função e marcará $scopecomo suja.
  3. Uma função que será executada se o inspetor estiver sujo.

Como os observadores são definidos

Existem muitas maneiras diferentes de definir um observador no AngularJS.

  • Você pode explicitamente $watchum attributeno $scope.

    $scope.$watch('person.username', validateUnique);
  • Você pode colocar uma {{}}interpolação no seu modelo (um observador será criado para você no atual $scope).

    <p>username: {{person.username}}</p>
  • Você pode solicitar uma diretiva ng-modelpara definir o inspetor para você.

    <input ng-model="person.username" />

O $digestciclo verifica todos os observadores em relação ao seu último valor

Quando interagimos com o AngularJS pelos canais normais (ng-model, ng-repeat, etc.), um ciclo de resumo será acionado pela diretiva.

Um ciclo de resumo é uma travessia profunda de $scopetodos os seus filhos . Para cada um $scope object, iteramos sobre ela $$watchers arraye avaliamos todas as expressões. Se o novo valor da expressão for diferente do último valor conhecido, a função do observador será chamada. Essa função pode recompilar parte do DOM, recalcular um valor $scope, acionar um AJAX request, qualquer coisa que você precise fazer.

Todo escopo é percorrido e toda expressão de observação é avaliada e verificada em relação ao último valor.

Se um observador for acionado, ele $scopeestará sujo

Se um observador for acionado, o aplicativo saberá que algo mudou e $scopeestá marcado como sujo.

As funções do observador podem alterar outros atributos nos $scopepais $scope. Se uma $watcherfunção foi acionada, não podemos garantir que nossas outras $scopes ainda estejam limpas e, portanto, executamos todo o ciclo de digestão novamente.

Isso ocorre porque o AngularJS possui ligação bidirecional, para que os dados possam ser transmitidos de volta para a $scopeárvore. Podemos alterar um valor para um valor mais alto $scopeque já foi digerido. Talvez nós alteremos um valor no $rootScope.

Se $digestestiver sujo, executamos o $digestciclo inteiro novamente

Percorremos o $digestciclo continuamente até que o ciclo de digestão seja limpo (todas as $watchexpressões têm o mesmo valor que tinham no ciclo anterior) ou atingimos o limite de digestão. Por padrão, esse limite é definido em 10.

Se atingirmos o limite de resumo, o AngularJS gerará um erro no console:

10 $digest() iterations reached. Aborting!

O resumo é difícil para a máquina, mas fácil para o desenvolvedor

Como você pode ver, sempre que algo muda em um aplicativo AngularJS, o AngularJS verifica todos os observadores da $scopehierarquia para ver como responder. Para um desenvolvedor, este é um benefício enorme da produtividade, como agora você precisa escrever quase nenhum código de fiação, o AngularJS notará apenas se um valor foi alterado e tornará o restante do aplicativo consistente com a alteração.

Do ponto de vista da máquina, embora isso seja extremamente ineficiente e reduzirá a velocidade do aplicativo, se criarmos muitos observadores. Misko citou uma cifra de cerca de 4000 observadores antes que seu aplicativo pareça lento em navegadores mais antigos.

É fácil atingir esse limite se você ng-repeatexceder um valor grande, JSON arraypor exemplo. Você pode atenuar isso usando recursos como ligação única para compilar um modelo sem criar observadores.

Como evitar a criação de muitos observadores

Sempre que seu usuário interage com seu aplicativo, cada observador em seu aplicativo será avaliado pelo menos uma vez. Uma grande parte da otimização de um aplicativo AngularJS é reduzir o número de observadores em sua $scopeárvore. Uma maneira fácil de fazer isso é com uma ligação única .

Se você possui dados que raramente mudam, é possível vinculá-los apenas uma vez usando a sintaxe ::, assim:

<p>{{::person.username}}</p>

ou

<p ng-bind="::person.username"></p>

A ligação será acionada apenas quando o modelo contido for renderizado e os dados carregados $scope.

Isso é especialmente importante quando você tem um ng-repeatitem com muitos itens.

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>
superluminário
fonte
Obrigado @ user2864740 - embora seja certo que a resposta de Misko seja a melhor. Ele conhece o quadro melhor do que ninguém, e é muito legal que ele se envolve com estouro de pilha ..
superluminary
4
Não concordo que a resposta deva estar no topo; existe uma diferença entre saber algo e escrever uma resposta relevante / detalhada para uma pergunta específica. Existem melhores maneiras de obter elogios. De qualquer forma ..
user2864740 01/01
1
Eu não duvido tal é verdade, mas questões perguntas e respostas respostas :)
user2864740
3
Boa resposta, cobrindo como o cheque sujo se comporta e o que está realmente avaliando, uma coisa não ficou muito clara na resposta de Misko.
Strider
3
Resposta excelente e detalhada. @ superluminary, obrigado por essa resposta. Além disso, depois de ler esta resposta, chego ao ponto em que não devemos adicionar expressões não-idempotentes como sendo vistas.
Mangu Singh Rajpurohit 01/07/19
81

Este é o meu entendimento básico. Pode muito bem estar errado!

  1. Os itens são observados passando uma função (retornando a coisa a ser observada) ao $watchmétodo.
  2. As alterações nos itens monitorados devem ser feitas dentro de um bloco de código agrupado pelo $applymétodo.
  3. No final do $applyo $digestmétodo é invocado que passa por cada um dos relógios e verifica se eles mudaram desde a última vez que o $digestran.
  4. Se alguma alteração for encontrada, o resumo será chamado novamente até que todas as alterações sejam estabilizadas.

No desenvolvimento normal, a sintaxe de ligação de dados no HTML diz ao compilador AngularJS para criar os relógios para você e os métodos do controlador $applyjá são executados dentro dele . Portanto, para o desenvolvedor do aplicativo, tudo é transparente.

Pete BD
fonte
4
quando o método de aplicação é acionado?
Numan salati 1/08/12
3
@EliseuMonar O loop digest é executado como resultado de algum evento ou chamado $ apply (), não é chamado periodicamente com base em um timer. veja Como funciona a função $ watch do AngularJS? e como a ligação e a digestão funcionam no AngularJS?
ADL
1
@remi, não estou preocupado com a última versão do AngularJS. Eles já estão usando proxies ou Object.observe? Caso contrário, eles ainda estão na era da verificação suja, que cria um loop temporizado para verificar se os atributos do modelo foram alterados.
Eliseu Monar dos Santos
1
Eu li que digest será executado um máximo de dez vezes sitepoint.com/understanding-angulars-apply-digest
user137717
62

Eu me perguntei isso por um tempo. Sem setters, como o AngularJSaviso muda para o $scopeobjeto? Ele os pesquisa?

Na verdade, o que ele faz é o seguinte: qualquer local "normal" para o qual você modifica o modelo já foi chamado a partir das tripas AngularJS, portanto ele o chama automaticamente $applyapós a execução do código. Digamos que seu controlador tenha um método conectado a ng-clickalgum elemento. Como AngularJSliga a chamada desse método para você, ele tem a chance de fazer um $applyno local apropriado. Da mesma forma, para expressões que aparecem diretamente nas visualizações, essas são executadas por AngularJSisso $apply.

Quando a documentação fala sobre precisar chamar $applymanualmente o código foraAngularJS dele, está falando sobre o código que, quando executado, não se origina AngularJSna pilha de chamadas.

jpsimons
fonte
32

Explicando com imagens:

A vinculação de dados precisa de um mapeamento

A referência no escopo não é exatamente a referência no modelo. Quando você vincula dois objetos a dados, precisa de um terceiro que escute o primeiro e modifique o outro.

insira a descrição da imagem aqui

Aqui, quando você modifica o <input>, você toca em data-ref3 . E o mecanismo clássico de ligação de dados mudará data-ref4 . Então, como as outras {{data}}expressões se moverão?

Eventos levam a $ digest ()

insira a descrição da imagem aqui

Angular mantém um oldValuee newValuede cada ligação. E após cada evento Angular , o famoso $digest()loop verifica a WatchList para ver se algo mudou. Estes eventos angulares são ng-click, ng-change, $httpconcluída ... O $digest()circuito vontade, desde que nenhum oldValuedifere do newValue.

Na figura anterior, ele notará que data-ref1 e data-ref2 foram alterados.

Conclusões

É um pouco como o ovo e a galinha. Você nunca sabe quem começa, mas espero que funcione na maioria das vezes como esperado.

O outro ponto é que você pode entender facilmente o impacto profundo de uma ligação simples na memória e na CPU. Espero que os desktops sejam gordos o suficiente para lidar com isso. Os telefones celulares não são tão fortes.

Nicolas Zozol
fonte
22

Obviamente, não há verificação periódica Scopese há alguma alteração nos objetos anexados a ele. Nem todos os objetos anexados ao escopo são observados. O escopo mantém prototipicamente um observador $$ . Scopesomente itera $$watchersquando $digesté chamado.

Angular adiciona um observador aos observadores $$ para cada um desses

  1. {{expression}} - Nos seus modelos (e em qualquer outro lugar onde haja uma expressão) ou quando definimos o modelo ng.
  2. $ scope. $ watch ('expressão / função') - Em seu JavaScript, podemos apenas anexar um objeto de escopo para angular assistir.

A função $ watch possui três parâmetros:

  1. O primeiro é uma função de observador que apenas retorna o objeto ou podemos apenas adicionar uma expressão.

  2. O segundo é uma função de ouvinte que será chamada quando houver uma alteração no objeto. Todas as coisas como alterações no DOM serão implementadas nesta função.

  3. O terceiro é um parâmetro opcional que recebe um booleano. Se for verdadeiro, o angular profundo observa o objeto e se o falso angular faz apenas uma referência ao objeto. A implementação aproximada de $ watch se parece com isso

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};

Existe uma coisa interessante no Angular chamada Digest Cycle. O ciclo $ digest começa como resultado de uma chamada para $ scope. $ Digest (). Suponha que você altere um modelo $ scope em uma função de manipulador por meio da diretiva ng-click. Nesse caso, o AngularJS aciona automaticamente um ciclo $ digest chamando $ digest (). Além do ng-click, existem várias outras diretivas / serviços integrados que permitem alterar modelos (por exemplo, ng-model, $ timeout, etc.) e acionar automaticamente um ciclo de digestão $. A implementação grosseira do $ digest é assim.

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };

Se usarmos a função setTimeout () do JavaScript para atualizar um modelo de escopo, o Angular não tem como saber o que você pode alterar. Nesse caso, é nossa responsabilidade chamar $ apply () manualmente, o que aciona um ciclo de digestão $. Da mesma forma, se você tiver uma diretiva que configure um ouvinte de eventos DOM e altere alguns modelos dentro da função manipuladora, precisará chamar $ apply () para garantir que as alterações entrem em vigor. A grande idéia do $ apply é que podemos executar algum código que não esteja ciente do Angular, que ainda pode mudar as coisas no escopo. Se envolvermos esse código em $ apply, ele chamará $ digest (). Implementação aproximada de $ apply ().

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};
Sasank Sunkavalli
fonte
15

O AngularJS lida com o mecanismo de ligação de dados com a ajuda de três funções poderosas: $ watch () , $ digest () e $ apply () . Na maioria das vezes, o AngularJS chama os $ scope. $ Watch () e $ scope. $ Digest (), mas, em alguns casos, pode ser necessário chamar essas funções manualmente para atualizar com novos valores.

$ watch () : -

Esta função é usada para observar alterações em uma variável no escopo $. Ele aceita três parâmetros: expressão, ouvinte e objeto de igualdade, em que ouvinte e objeto de igualdade são parâmetros opcionais.

$ digest () -

Essa função interage com todos os relógios no objeto $ scope e seus objetos filho $ scope
(se houver algum). Quando $ digest () itera nos relógios, ele verifica se o valor da expressão mudou. Se o valor foi alterado, AngularJS chama o ouvinte com novo valor e valor antigo. A função $ digest () é chamada sempre que o AngularJS achar necessário. Por exemplo, após um clique no botão ou após uma chamada AJAX. Você pode ter alguns casos em que o AngularJS não chama a função $ digest () para você. Nesse caso, você tem que chamá-lo você mesmo.

$ apply () -

O Angular atualiza automaticamente apenas as alterações de modelo que estão dentro do contexto do AngularJS. Quando você altera qualquer modelo fora do contexto Angular (como eventos DOM do navegador, setTimeout, XHR ou bibliotecas de terceiros), é necessário informar o Angular das alterações chamando $ apply () manualmente. Quando a chamada da função $ apply () termina, o AngularJS chama $ digest () internamente, para que todas as ligações de dados sejam atualizadas.

Bharath Kumar
fonte
7

Aconteceu que eu precisava vincular um modelo de dados de uma pessoa a um formulário, o que fiz foi um mapeamento direto dos dados com o formulário.

Por exemplo, se o modelo tivesse algo como:

$scope.model.people.name

A entrada de controle do formulário:

<input type="text" name="namePeople" model="model.people.name">

Dessa forma, se você modificar o valor do controlador de objeto, isso será refletido automaticamente na visualização.

Um exemplo em que passei o modelo é atualizado a partir dos dados do servidor é quando você solicita um CEP e um CEP com base em cargas escritas carrega uma lista de colônias e cidades associadas a essa exibição e, por padrão, define o primeiro valor com o usuário. E isso funcionou muito bem, o que acontece é que, angularJSàs vezes, leva alguns segundos para atualizar o modelo; para fazer isso, você pode colocar um botão giratório enquanto exibe os dados.

gartox
fonte
14
Li essa resposta 5 vezes e ainda não entendi o que significa aqui.
Sbedulin
1
A resposta parece um quebra-cabeça para mim
Aman /
6
  1. A ligação de dados unidirecional é uma abordagem em que um valor é obtido do modelo de dados e inserido em um elemento HTML. Não há como atualizar o modelo da visualização. É usado em sistemas de gabaritos clássicos. Esses sistemas vinculam dados em apenas uma direção.

  2. A ligação de dados em aplicativos Angular é a sincronização automática de dados entre o modelo e os componentes de exibição.

A ligação de dados permite tratar o modelo como a única fonte de verdade em seu aplicativo. A vista é uma projeção do modelo em todos os momentos. Se o modelo for alterado, a visualização refletirá a mudança e vice-versa.

Shankar Gangadhar
fonte
5

Aqui está um exemplo de ligação de dados com o AngularJS, usando um campo de entrada. Vou explicar mais tarde

Código HTML

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

Código AngularJS

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

Como você pode ver no exemplo acima, o AngularJS usa ng-modelpara ouvir e assistir o que acontece nos elementos HTML, especialmente nos inputcampos. Quando algo acontecer, faça alguma coisa. No nosso caso, ng-modelestá vinculado à nossa visão, usando a notação bigode {{}}. Tudo o que é digitado dentro do campo de entrada é exibido na tela instantaneamente. E essa é a beleza da ligação de dados, usando o AngularJS em sua forma mais simples.

Espero que isto ajude.

Veja um exemplo de trabalho aqui no Codepen

AllJs
fonte
5

AngularJs suporta ligação de dados bidirecional .
Significa que você pode acessar os dados Ver -> Controlador e Controlador -> Ver

Para Ex.

1)

// If $scope have some value in Controller. 
$scope.name = "Peter";

// HTML
<div> {{ name }} </div>

O / P

Peter

Você pode ligar dados no ng-modelLike: -
2)

<input ng-model="name" />

<div> {{ name }} </div>

Aqui no exemplo acima, qualquer que seja o usuário de entrada, será visível na <div>tag.

Se desejar vincular a entrada do html ao controlador: -
3)

<form name="myForm" ng-submit="registration()">
   <label> Name </lbel>
   <input ng-model="name" />
</form>

Aqui, se você deseja usar a entrada nameno controlador,

$scope.name = {};

$scope.registration = function() {
   console.log("You will get the name here ", $scope.name);
};

ng-modelvincula nossa visão e a expressa {{ }}.
ng-modelsão os dados mostrados ao usuário na visualização e com os quais o usuário interage.
Portanto, é fácil vincular dados em AngularJs.

ojus kulkarni
fonte
4

O Angular.js cria um observador para cada modelo que criamos em exibição. Sempre que um modelo é alterado, uma classe "ng-dirty" é anexada ao modelo, para que o observador observe todos os modelos com a classe "ng-dirty" e atualize seus valores no controlador e vice-versa.

Shankar Gangadhar
fonte
3

ligação de dados:

O que é ligação de dados?

Sempre que o usuário altera os dados na visualização, ocorre uma atualização dessa alteração no modelo de escopo e vice-versa.

Como isso é possível?

Resposta curta: Com a ajuda do ciclo de digestão.

Descrição: js angulares define o inspetor no modelo de escopo, que aciona a função de ouvinte se houver uma alteração no modelo.

$scope.$watch('modelVar' , function(newValue,oldValue){

// Código de atualização do Dom com novo valor

});

Então, quando e como é chamada a função de observador?

A função de observador é chamada como parte do ciclo de digestão.

O ciclo de resumo é chamado automaticamente acionado como parte de js angulares construídos em diretivas / serviços como ng-model, ng-bind, $ timeout, ng-click e outros .. que permitem ativar o ciclo de resumo.

Função do ciclo digestivo:

$scope.$digest() -> digest cycle against the current scope.
$scope.$apply() -> digest cycle against the parent scope 

ie$rootScope.$apply()

Nota: $ apply () é igual a $ rootScope. $ Digest () significa que a verificação suja começa diretamente da raiz ou do topo ou do escopo pai até todos os escopos filho $ no aplicativo js angular.

Os recursos acima também funcionam nos navegadores IE para as versões mencionadas, apenas certificando-se de que seu aplicativo seja um aplicativo js angular, o que significa que você está usando o arquivo de script de estrutura angularjs mencionado na tag de script.

Obrigado.

Dhana
fonte