A mágica por trás do Angular: o $digest

Postado por em   ●   1 comentário

De todas as funcionalidades que o Angular nos oferece, a que ganhou maior destaque e admiração do público foi a “mágica” do data binding (mais especificamente o two-way data binding), que atualiza as informações ao vivo na página.

Hoje nós vamos desvendar essa mágica e entender direitinho como o Angular consegue fazer essa proeza. Então, com o perdão do pleonasmo, vamos começar do começo.

Mas antes, veja o exemplo abaixo em funcionamento (digite um nome no campo):

 

See the Pen AngularJS Two-way Data Binding by AlgaWorks (@algaworks) on CodePen.light

 

Estendendo os eventos do navegador

Os navegadores estão sempre aguardando por eventos. É por isso que eles sabem quando clicamos em um botão, ou escrevemos numa caixa de texto.

Esses eventos executam funções que podemos registrar e assim chamar um script em resposta à ação do usuário. Vamos ver isso funcionando com JavaScript puro:

 

See the Pen oLxVzd by AlgaWorks (@algaworks) on CodePen.light

 

O Angular estende todos esses eventos, criando o seu próprio contexto, o “angular context”.

A Lista de Observação

Toda vez que você faz um data binding no Angular você, sem saber, coloca um $watch – uma observação – na lista lista de observação do Angular. Ou seja, o Angular faz uma lista das propriedades que ele tem que ficar observando se tem alguma mudança.

Então, quando fazemos algo assim:

Ou assim:

Estamos colocando um $watch com essa variável “nome” na lista de observação.

Tá bom, então toda vez que fazemos um data bind estamos criando um $watch e colocando na lista de observação. E agora?

O ciclo digestivo do Angular

Ok, lembra daquele contexto próprio do Angular nos eventos do navegador?

Então, toda vez que o navegador dispara um evento que pode ser gerenciado pelo angular context, um loop chamado $digest é colocado pra rodar.

Este $digest é composto por dois loops menores, um pra rodar a lista de $evalAsync – que não vamos tratar neste post – e um outro pra processar a Lista de Observação.

E como seria esse processo? O loop vai passar por cada $watch e retirar duas informações:

  • Houve alteração no valor da variável?
  • Qual o novo valor da variável?

Quando terminar todos os $watch, ele vai passar mais uma vez pra conferir se está tudo do mesmo jeito, se tiver alguma alteração, ele roda de novo e de novo (até 10 vezes, depois disso ele solta uma exception) até ele ter certeza que está tudo certinho, então o loop acaba e ele faz as alterações na interface.

Isso é chamado de dirty-checking ou “conferência suja”, porque ele precisa conferir um por um de cada $watch, não apenas o que foi alterado. Isso normalmente demora uma fração de segundo, mas quanto mais $watch no projeto, menos rápido fica.

Aplicando o contexto

Isso quer dizer que CADA ação do usuário, cada tecla apertada, cada clique do mouse roda um $digest?

Ainda bem que não. Na verdade, o $digest só vai rodar se aquele evento estiver dentro do Angular Context.

E como saber se o evento está dentro do contexto do Angular? A resposta é o $apply.

Se você chamar o método $apply quando um evento é disparado, ele vai rodar dentro do Angular Context e vai disparar o $digest.

O Angular vem com um monte de diretivas e métodos personalizados que chamam o $apply pra gente automaticamente, como o ng-click, o $timeout, etc.

Vamos ver então como fica um bind sem o $apply:

 

See the Pen Sem $apply by AlgaWorks (@algaworks) on CodePen.light

 

Veja que a mensagem nunca aparece porque o $digest não é chamado. Agora com o $apply:

 

See the Pen Com $apply by AlgaWorks (@algaworks) on CodePen.light

 

Quando o conteúdo do $apply é executado, ele chama automaticamente o $digest e o nosso bind é atualizado. Isso não quer dizer que sem o $apply o bind não vai atualizar nunca, um outro elemento que chamar o $digest vai fazer a atualização do conteúdo, olha só:

 

See the Pen Sem $apply c/ Botão by AlgaWorks (@algaworks) on CodePen.light

 

Ao usar a diretiva ng-click no botão, o Angular já colocou o evento click dele no Angular Context e o $digest vai ser chamado.

Então lembre-se: sempre que você fizer alguma alteração no escopo através de um evento que não está dentro do contexto do Angular, como um callback de algum componente do jQuery por exemplo, use o $apply pra levar essa alteração para o Angular Context.

Conclusão

Pronto! Agora que a “mágica” do data-binding do Angular foi desvendada você estará muito mais preparado para lidar com um dos “problemas” mais reportados com Angular na internet: “bindings que não funcionam”.

Legal, né? Para não perder dicas como essa e ficar por cima das últimas novidades de desenvolvimento Front-end do mercado, assine nossa Lista VIP de Front-end agora! Não se preocupe, não mandamos spam. :)

Faça parte também da nossa Comunidade Front-end no Facebook.

Um abraço e até mais!

Lista VIP de Front-end

Professor front-end da AlgaWorks. Trabalhando na área de desde 1998, acompanhou a evolução do desenvolvimento web desde o início do HTML 4, onde o CSS e o JavaScript eram lendas.

1 comentário sobre “A mágica por trás do Angular: o $digest

  1. Elton -

    Muito interessante!

Deixe um comentário