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:
constructor()
OnInit
DoCheck
OnChanges
OnDestroy
E estes são os ganchos para os componentes filhos de um componente:
AfterContentInit
AfterContentChecked
AfterViewInit
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:
- Ele atualiza as ligações de entrada de seus componentes filhos
- Executa
OnChanges
se houver alterações em seus adereços de entrada - Executa
OnInit
nos componentes filhos, caso ainda não tenha executado - Executa
DoCheck
nos componentes filhos - 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 .