Ganchos de ciclo de vida são uma funcionalidade especial no Angular que nos permite”conectar”e executar código em um evento de ciclo de vida específico de um componente ou diretiva.

O Angular gerencia componentes e diretivas para nós ao criá-los, atualizá-los ou destruí-los. Com ganchos de ciclo de vida, podemos obter melhor controle de nosso aplicativo.

Para fazer isso, adicionamos alguns métodos de gancho específicos prefixados com ng ao nosso componente ou diretiva. Esses ganchos são divididos em dois tipos: ganchos para componentes ou diretivas e ganchos para componentes filhos.

O que aprenderemos

Este artigo cobrirá o seguinte:

  • Os diferentes ganchos de ciclo de vida para componentes e diretivas angulares
  • Como se conectar aos ciclos de vida dos componentes para executar nosso código
  • O que aciona os ganchos do ciclo de vida e a ordem em que são chamados

Estes são os ganchos para componentes ou diretivas, na ordem da chamada:

  1. constructor()
  2. OnInit
  3. DoCheck
  4. OnChanges
  5. OnDestroy

E estes são os ganchos para os componentes filhos de um componente:

  1. AfterContentInit
  2. AfterContentChecked
  3. AfterViewInit
  4. AfterViewChecked

Abaixo está um resumo dos ganchos do ciclo de vida Angular em uma tabela:

OnInit Chamado na inicialização
OnChanges Chamado quando as propriedades de entrada foram alteradas
DoCheck Detecção de alterações personalizadas do desenvolvedor
Destroy Chamado antes de o componente ser destruído
AfterContentInit Chamado quando o conteúdo do componente ngContent é inicializado
AfterContentChecked Chamado quando o conteúdo do componente é atualizado ou verificado quanto a atualizações
AfterViewInit Chamado quando a vista projetada do componente foi inicializada
AfterViewChecked Chamado após a visualização projetada ter sido verificada

Ganchos para componentes ou diretivas

No Angular, os componentes são as menores unidades de blocos de construção de uma árvore de componentes. Isso nos permite desenvolver e manter nossa base de código com muita facilidade. Componentes são classes com o decorador @Component () neles, assim:

  @Component ({ seletor:"app-comp", modelo: `
AppComponent
`, estilos: `` }) classe AppComponent implementa OnInit { //... }

As diretivas são tecnicamente componentes, mas operam na parte que não é da IU do DOM. Seu trabalho é adicionar funcionalidade extra ao DOM. As diretivas também são classes, mas decoradas com o decorador de classe @Directive () .

Exemplos de diretivas embutidas são * ngFor , * ngIf , etc. Podemos criar nossa própria diretiva fazendo isto:

  @Directive ({ seletor:'[texto em destaque]'
})
export class HighlightDirective { //...
}
 

A seguir estão os ganchos do ciclo de vida para componentes ou diretivas, com explicações sobre como funcionam e como usá-los.

Constructor()

Este é o construtor com o qual estamos familiarizados nas classes JS. Ele está incluído porque nossos componentes/diretivas são classes JavaScript com um decorador @Component ou @Directive . Portanto, este construtor é chamado quando nossa instância de componente/diretiva é criada pelo Angular usando a palavra-chave new .

OnInit

OnInit é um gancho de ciclo de vida que é chamado depois que o Angular inicializou todas as propriedades vinculadas a dados de uma diretiva. Defina um método ngOnInit () para lidar com qualquer tarefa de inicialização adicional.

Este gancho é chamado quando a primeira detecção de alteração é executada no componente. Após a primeira execução, ele é desligado para nunca mais ser executado. É uma prática recomendada adicionar nossa lógica de inicialização aqui.

Para entender isso, implementaremos a interface OnInit e adicionaremos o método ngOnInit em nosso componente/diretiva:

  @Component ({
//...
})
export class BComponent implementa OnInit { ngOnInit () { console.log ("ngOnInit chamado") }
}
 

Qual é a diferença entre constructor e OnInit ?

Construtor e OnInit parecem iguais, mas são chamados em fases diferentes. O Angular tem duas fases: a fase de inicialização e a fase de detecção de alterações.

O construtor é chamado na fase de inicialização. Esta fase é quando o Angular cria as instâncias de serviços, canais, componentes e diretivas em nosso módulo. O Angular inicializa o componente, resolve suas dependências e o passa para o construtor.

A fase de detecção de alterações ocorre após a fase de bootstrap quando o Angular finalmente chama o método tick ApplicationRef # tick .

O que é detecção de alterações? Este é um mecanismo pelo qual o Angular detecta uma mudança no estado de um componente/diretiva e atualiza o DOM para refletir como tal. O Angular detecta isso usando Zone.js , uma biblioteca que corrige as seguintes APIs assíncronas no navegador:

  • setTimeout , clearTimeout , setInterval , etc.
  • AJAX: XMLHttpRequest

Na fase de detecção de mudança, esses ganchos são chamados em sequência:

  • OnInit
  • DoCheck
  • OnChanges

Prova:

 //https://github.com/angular/packages/core/src/view/provider.ts
função de exportação checkAndUpdateDirectiveDynamic ( view: ViewData, def: NodeDef, values: any []): boolean { const providerData=asProviderData (view, def.nodeIndex); diretiva const=providerData.instance; deixe alterado=falso; deixe as mudanças: SimpleChanges=undefined!; para (deixe i=0; i  

Assim, o OnInit é chamado depois que a árvore de componentes foi construída e as dependências são resolvidas e passadas para as instâncias do componente/diretiva. Depois de ser chamado, ele é desligado.

O que queremos dizer com estar desligado? Cada componente/diretiva em Angular tem estados de exibição:

  export const enum NodeFlags { OnInit=1 <<16, OnDestroy=1 <<17, DoCheck=1 <<18, OnChanges=1 <<19, AfterContentInit=1 <<20, AfterContentChecked=1 <<21, AfterViewInit=1 <<22, AfterViewChecked=1 <<23,
//...
}
 

O acima é uma máscara de bits de um estado de exibição. Internamente, os componentes/diretivas são chamados de visualização. A visualização de cada componente é criada com estados de visualização, dependendo do gancho de ciclo de vida implementado. Um estado de exibição pode ser a combinação de qualquer uma das máscaras de bits acima. Observe que cada gancho tem uma máscara de bits.

Como nosso componente BComponent implementa o OnInit , seu estado de exibição será OnInit=1 <<16 .
Quando o gancho OnInit é chamado em um componente/diretiva, o Angular verifica se é a primeira vez. Se for, ele atualiza a máscara de estado para ser verdadeira e chama o método ngOnInit :

  if ((def.flags & NodeFlags.OnInit) && shouldCallLifecycleInitHook (view, ViewState.InitState_CallingOnInit, def.nodeIndex)) { directiva.ngOnInit (); }
 

Portanto, na próxima execução do CD, o acima sempre será falso e a chamada ngOnInit será ignorada.

OnChanges

OnChanges é um gancho de ciclo de vida que é chamado quando qualquer propriedade ligada a dados de uma diretiva muda. Um método ngOnChanges () deve ser definido para lidar com as mudanças.

Digamos que nosso BComponent tenha a propriedade input books :

  @Component ({ seletor:'bcomp', template: ` 
{{livro}}
` }) export class BComponent implementa OnChanges { Livro @Input () ngOnChanges () { console.log ("A propriedade do livro foi alterada") } } @Componente({ //... template: ` ` }) export class App { books=["Michael Crichton: Prey"] }

App vinculou sua propriedade books aos livros de BComponent . Observe como implementamos a interface OnChanges e adicionamos um método ngOnChanges . ngOnChanges será chamado pelo Angular quando a propriedade de livros que ele recebe do componente App for alterada.

Se alterarmos a propriedade books no componente App , o Angular não irá pegá-lo e o ngOnChanges no BComponent não será executado:

  @Component ({
//... template: `   `
})
export class App { books=["Michael Crichton: Prey"] construtor () { setInterval (()=> { this.books.push ("Novo livro:"+ Date.now ()) }, 1000) }
}
 

O método push é um método mutante e, com o código acima, BComponent não selecionará nenhuma alteração e ngOnChanges não será correr.

Como o BComponent não é uma estratégia de CD OnPush , sua visualização será atualizada, mas o novo livro adicionado a books não será exibido , porque o Angular atualiza as ligações de entrada de um componente/diretiva antes de atualizá-lo no DOM.

Devemos estar cientes da imutabilidade e sempre tentar retornar um novo estado/referência. Usaremos um método não mutante chamado Array # concat :

  @Component ({
//... template: `   `
})
export class App { books=["Michael Crichton: Prey"] construtor () { setInterval (()=> { this.books=this.books.concat (["Novo livro:"+ Date.now ()]) }, 1000) }
}
 

Agora, o ngOnChanges no BComponent será executado.

Muito simplesmente, ngOnChanges é executado quando as ligações de entrada do componente/diretiva mudam.

DoCheck

DoCheck é um método de retorno de chamada que realiza a detecção de alterações, invocado após a execução do detector de alterações padrão.

Este gancho vem após o gancho OnInit . DoCheck não é executado em um evento como OnInit e OnChanges , que são chamados quando ocorre uma alteração nas propriedades de entrada ou quando o componente/diretiva é inicializado. Em vez disso, esse gancho é adicionado para que o desenvolvedor possa adicionar seu código personalizado para executar um CD personalizado.

DoCheck invoca uma função de detecção de mudança personalizada para uma diretiva, além da verificação realizada pelo detector de mudança padrão:

  if (def.flags & NodeFlags.DoCheck) { diretiva.ngDoCheck (); }
 

Como vimos acima, este gancho é executado constantemente em cada ciclo do CD, seja o componente/diretiva um componente OnPush ou Default . O fato de seu gancho ngDoCheck ser chamado não significa que seu componente/diretiva está sendo verificado, apenas que o gancho é chamado:

  @Component ({ seletor:'bcomp', template: ` 
{{livro}}
`, changeDetection: ChangeDetectionStrategy.OnPush }) export class BComponent implementa OnChanges { Livro @Input () ngOnChanges () { console.log ("A propriedade do livro foi alterada") } } @Componente({ //... template: ` ` }) export class App { books=["Michael Crichton: Prey"] }

O BComponent é um componente OnPush , o que significa que será verificado primeiro por alterações em suas ligações de entrada e, em seguida, em execuções de CD subsequentes, o componente e seus filhos será ignorado para uma verificação. A estratégia de CD OnPush é uma técnica de otimização que apenas renderiza o componente quando necessário ou quando suas ligações de entrada foram alteradas.

Se o BComponent implementa DoCheck e adiciona o método ngDoCheck , ele sempre será executado mesmo quando o CD executado nele for ignorado:

  @Component ({ seletor:'bcomp', template: ` 
{{livro}}
`, changeDetection: ChangeDetectionStrategy.OnPush }) export class BComponent implementa OnChanges, DoCheck { Livro @Input () ngOnChanges () { console.log ("A propriedade do livro foi alterada") } ngDoCheck () { console.log ("ngDoCheck em BCompoent chamado") } } @Componente({ //... template: ` ` }) export class App { books=["Michael Crichton: Prey"] construtor () { setInterval (()=> { this.books.push ("Nome do livro:"+ Date.now ()) }, 1000) } }

Na primeira execução do CD, BComponent será renderizado novamente e veremos o seguinte registrado em nosso console:

  A propriedade do livro foi alterada
ngDoCheck no BCompoent chamado
 

Conforme o componente App começa a atualizar a propriedade books, ele mesmo ( App ) será atualizado, mas o CD executado no BComponent será ser ignorado porque o Angular não verá nenhuma alteração na matriz BComponent books .

Ainda, veremos ngDoCheck e BCompoent chamados no console. Isso ocorre devido à forma como o Angular é estruturado.

Quando o CD é executado em um componente, ele passa por cinco etapas:

  1. Ele atualiza as ligações de entrada de seus componentes filhos
  2. Executa OnChanges se houver alterações em seus adereços de entrada
  3. Executa OnInit nos componentes filhos, caso ainda não tenha executado
  4. Executa DoCheck nos componentes filhos
  5. Executa sua função updateRenderer para atualizar as interpolações DOM

DoCheck não tem nenhuma condição antes de ser executado, portanto, deve ser executado até que o componente/diretiva implemente a interface DoCheck .

Se App usa o método Array # concat para enviar um novo livro, sempre veremos o seguinte, porque concat retorna um novo referência do objeto:

  A propriedade do livro foi alterada
ngDoCheck no BCompoent chamado
 

Qual é a utilidade de DoCheck ?

Como funciona sem nenhuma condição, podemos usar DoCheck para adicionar um mecanismo de CD personalizado nosso.

O algoritmo de detecção de alterações padrão procura diferenças comparando os valores das propriedades vinculadas por referência nas execuções de detecção de alterações. Você pode usar este gancho para verificar e responder às mudanças por outros meios.

Quando o detector de alterações padrão detecta alterações, ele invoca ngOnChanges se fornecido, independentemente de você realizar detecção de alterações adicional. Normalmente, você não deve usar DoCheck e OnChanges para responder às alterações na mesma entrada.

Idealmente, o Angular oferece suporte e prega a imutabilidade em nossos objetos, mas com ngDoCheck , podemos rastrear mutações de objeto.

Usando nosso exemplo:

  @Component ({ seletor:'bcomp', template: ` 
{{livro}}
`, changeDetection: ChangeDetectionStrategy.OnPush }) export class BComponent { Livro @Input () } @Componente({ //... template: ` ` }) export class App { books=["Michael Crichton: Prey"] construtor () { setInterval (()=> { this.books.push ("Nome do livro:"+ Date.now ()) }, 1000) } }

Como o componente App está enviando um novo valor usando o método Array # push , o Angular não perceberá a mudança porque Array # push altera o array books . Assim, o BComponent não pegará a mudança. Aqui entra o ngDoCheck , porque podemos usá-lo para verificar a mutação do objeto.

O BComponent implementará a interface DoCheck e adicionará o método ngDoCheck . A seguir, injetaremos o ChangeDetectorRef para que BComponent possa acionar o CD quando seu CD personalizado detectar uma alteração:

  @Component ({ seletor:'bcomp', template: ` 
{{livro}}
`, changeDetection: ChangeDetectionStrategy.OnPush }) export class BComponent implementa OnChanges, DoCheck { Livros @Input () lastBooksArray construtor (private changeDetectorRef: ChnageDetectorRef) {} ngOnChanges () { console.log ("A propriedade do livro foi alterada") this.lastBooksArray=this.books } ngDoCheck () { console.log ("ngDoCheck em BCompoent chamado") if (this.lastBooksArray.length!=this.books.length) { this.changeDetectorRef.markForChange () } } } @Componente({ //... template: ` ` }) export class App { books=["Michael Crichton: Prey"] construtor () { setInterval (()=> { this.books.push ("Nome do livro:"+ Date.now ()) }, 1000) } }

Injetamos ChangeDetectorRef para conectar ao CD de BComponent , implementamos DoCheck e OnChanges e adicionamos Métodos ngDoCheck e ngOnChanges .

No método ngOnChanges , salvamos a última alteração do array books , então, quando o array sofrer mutação no componente App , podemos verificar para a mudança em ngDoCheck e marque o BComponent para o CD no próximo ciclo do CD.

OnDestroy

OnDestroy é o gancho do ciclo de vida que é chamado quando uma diretiva, canal ou serviço é destruído. Use isso para qualquer limpeza personalizada que precise ocorrer quando a instância for destruída.

Este gancho é usado principalmente para cancelar a assinatura de nossos fluxos observáveis ​​e desanexar manipuladores de eventos para evitar vazamentos de memória.

Ganchos para componentes filhos

O Angular é estruturado como uma árvore, então os componentes podem ter componentes pai e filho. Em outras palavras, o que está renderizando o componente e o que o componente está renderizando. Os ganchos que vamos aprender aqui são os ganchos que serão chamados em eventos de ciclo de vida específicos dos componentes filhos de um componente. Os ganchos são declarados no componente pai, para que os desenvolvedores possam adicionar seu código personalizado para aumentar a funcionalidade do componente.

Ganchos no componente pai são chamados quando o conteúdo do componente filho foi inicializado, quando a IU ou visualização do componente filho foi inicializada e renderizada ou quando a detecção de mudança do componente filho foi executada.

AfterContentInit

AfterContentInit é chamado quando o conteúdo de um componente/diretiva foi inicializado. Nesse caso, “conteúdo” é o componente/diretivas que são projetados entre as tags :

  @Component ({ seletor:'bcomp', template: ` 
Este é um BComponnet
` }) exportar classe BComponent {} @Componente({ seletor:'acomp', template: ` ` }) export class AComponent implementa AfterContentInit { ngAfterContentInit () { //... } } @Componente({ template: ` ` }) exportar classe App {}

O Acomponent projetará quaisquer elementos entre sua tag dentro da tag ng-content . Agora, no componente App , o BComponent é projetado na tag acomp . Quando o BComponent está sendo inicializado, o gancho ngAfterContentInit será chamado em AComponent .

AfterContentChecked

Este gancho é chamado depois que o detector de mudança padrão para o componente/diretiva projetada em um componente por meio da tag ng-content concluiu sua verificação:

  @Component ({ seletor:'acomp', template: `   `
})
export class AComponent implementa AfterContentInit { ngAfterContentInit () {
//... }
}
@Componente({ template: `  {{dados}}  `
})
export class App implementa AfterContentChecked { dados: qualquer ngAfterCOntentChecked () {
//... }
}
 

O componente App possui uma propriedade data que está dentro do AComponent . Quando a propriedade data muda, o método ngAfterContentChecked será chamado.

AfterViewInit

Este gancho é chamado depois que a visualização de um componente e as visualizações filhas foram criadas e totalmente inicializadas.

Este gancho é útil quando queremos fazer referência a uma instância de componente/diretiva em nosso componente usando ViewChild / ViewChildren .

Usá-lo em qualquer outro método pode causar um erro porque o componente/diretiva pode não ter sido inicializado no momento do uso:

  @Component ({ seletor:'acomp', template: ` `
})
export class AComponent { aCompMethod () {
//... }
}
@Componente({ template: `   `
})
export class App { @ViewChild ('acomp') acomp: AComponent construtor () { setInterval (()=> { this.acomp.aCompMethod () }, 1000); }
}
 

Conseguimos a referência da classe acomp AComponent colocando # no modelo de componente App .

Então, usamos ViewChild para dizer ao Angular para definir a instância de AComponent para a variável acomp . Podemos cair em erro se o AComponent não tiver sido inicializado quando solicitamos a instância do AComponent .

Se chamarmos o AComponent do aCompMethod em intervalos de um segundo, pode ocorrer um erro de referência porque o AComponent não foi inicializado em daquela vez. Com intervalos maiores, o aCompMethod será chamado sem erro porque, nessa altura, o AComponent será criado.

Para evitar essa perda de referência, implementaremos a interface AfterViewInit e adicionaremos o método ngAfterViewInit . Em seguida, chame o aCompMethod no ngAfterViewInit :

  @Component ({ seletor:'acomp',
//...
})
export class AComponent { aCompMethod () { }
}
@Componente({ template: `   `
})
export class App implementa AfterViewInit { @ViewChild ('acomp') acomp: AComponent ngAfterViewInit () {
//... setInterval (()=> { this.acomp.aCompMethod () }, 1000); }
}
 

Agora, não teremos uma falha de referência quando chamarmos o aCompMethod porque ele será chamado quando a classe AComponent for criada e inicializada.

AfterViewChecked

Este gancho é chamado após o detector de alterações de um componente filho de um componente/diretiva ter sido executado para verificações.

Tenha cuidado para não definir quaisquer variáveis ​​associadas ao modelo aqui. Se você fizer isso, receberá o erro "Expression has changed after it wasked".

  @Component ({ seletor:'acomp',
//...
})
export class AComponent { Dados @Input () aCompMethod () { return this.data * 9 }
}
@Componente({ template: ` 
{{aCompData}}
` }) export class App implementa AfterViewChecked { @ViewChild ('acomp') acomp: AComponent dados: qualquer acompData: any changeData () { this.data=Date.now () } ngAfterViewChecked () { //... this.acompData=this.acomp.aCompMethod () } }

AComponent tem uma ligação de entrada data , e alteramos a ligação de dados quando clicamos no botão changeData , pois queremos exibir resultado do resultado aCompMethod de AComponent no aCompData .

Se chamarmos o método em qualquer outro lugar, podemos encontrar um erro de referência porque o Angular pode não ser feito com o CD executado no AComponent . Portanto, implementamos a interface AfterViewChecked e adicionamos o método ngAfterViewInit porque o método será executado quando o AComponent tiver concluído a execução do CD.

Nesse caso, não obteremos um valor de dados incorreto quando chamarmos o aCompMethod , porque os dados terão o valor atual.

Conclusão

Esta é uma lista abrangente de eventos de ciclo de vida Angular com exemplos. Esta postagem explicou em detalhes quando ocorre cada evento do ciclo de vida, sua linha do tempo e o que faz com que sejam acionados.

Da próxima vez, se acontecer de você esquecer algum evento do ciclo de vida, você sempre pode voltar a este artigo.

Se você tiver alguma dúvida sobre isso ou qualquer coisa que eu deva acrescentar, sinta-se à vontade para deixar um comentário!

A postagem Ganchos de ciclo de vida angulares explicados apareceu primeiro em LogRocket Blog .

Source link