Ciclo de Vida de um Componente em Angular
Após a criação de um componente, o framework Angular chama uma série de métodos que representam diferentes etapas do ciclo de vida desse componente. A inicialização de um componente pode ser figurativamente representada da seguinte maneira:
Durante o processo de atualização de um componente, alguns eventos do ciclo de vida também são acionados:
Portanto, o componente passa pelas seguintes etapas do ciclo de vida:
Construtor
: Primeiro, o construtor do componente é executado.ngOnChanges
: Chamado antes do métodongOnInit()
durante a configuração inicial de propriedades vinculadas por meio do mecanismo de binding, e também em qualquer reinstalação ou alteração de seus valores. Este método recebe como parâmetro um objeto da classe Simp``leChanges, que contém os valores anteriores e atuais das propriedades. Por exemplo, verificação de alterações:ngOnChanges(changes: SimpleChanges) { for (const inputName in changes) { const inputValues = changes[inputName]; console.log(`Previous ${inputName} == ${inputValues.previousValue}`); console.log(`Current ${inputName} == ${inputValues.currentValue}`); console.log(`Is first ${inputName} change == ${inputValues.firstChange}`); } }
ngOnInit
: Chamado uma vez após o Angular inicializar todas as propriedades de entrada do componente com seus valores iniciais. Executado antes da inicialização do template do componente. Isso significa que, neste método, é possível atualizar o estado do componente com base em seus valores iniciais.ngDoCheck
: Chamado em cada verificação de alterações nas propriedades do componente, logo após os métodosngOnChanges
engOnInit
.ngAfterContentInit
: Chamado uma vez após o métodongDoCheck()
depois que todos os componentes filhos foram inicializados.ngAfterContentChecked
: Chamado pelo Angular ao verificar alterações no conteúdo inserido no template do componente. Executado após o métodongAfterContentInit()
e após cada chamada subsequente do métodongDoCheck()
.ngAfterViewInit
: Chamado pelo Angular após a inicialização do template do componente, bem como do template dos componentes filhos. Executado apenas uma vez, logo após a primeira chamada do métodongAfterContentChecked()
.ngAfterViewChecked
: Chamado pelo Angular após verificar mudanças no template do componente, assim como no template dos componentes filhos. Executado após a primeira chamada do métodongAfterViewInit()
e após cada chamada subsequente do métodongAfterContentChecked()
.ngOnDestroy
: Chamado antes de o Angular remover o componente.afterRender
eafterNextRender
: permitem executar código após a renderização do componente. O código dessas funções é chamado após o Angular concluir a renderização de todos os componentes na página no DOM. Essas funções se aplicam à aplicação como um todo e não a componentes individuais. Portanto, elas capturam o momento após a renderização de toda a aplicação e de todos os seus componentes.
A maioria desses métodos está definida em interfaces específicas, cujo nome corresponde ao nome do método sem o prefixo "ng". Por exemplo, o método ngOnInit
está definido na interface OnInit
. Portanto, se quisermos monitorar determinadas etapas do ciclo de vida de um componente, a classe do componente deve implementar as interfaces correspondentes:
import { Component, OnInit, OnDestroy } from "@angular/core";
@Component({
selector: "my-app",
standalone: true,
template: `<p>Hello Programício</p>`,
})
export class AppComponent implements OnInit, OnDestroy {
constructor() {
console.log("constructor");
}
ngOnInit() {
console.log("onInit");
}
ngOnDestroy() {
console.log("onDestroy");
}
}
ngOnInit
O método ngOnInit()
é utilizado para uma inicialização mais complexa do componente. Aqui, é possível realizar o carregamento de dados de um servidor ou de outras fontes de dados.
ngOnInit()
não é equivalente ao construtor. Embora o construtor possa realizar alguma inicialização do objeto, não é recomendável executar operações complexas nele. O construtor deve ser o mais simples possível e realizar apenas a inicialização básica. Operações mais complexas, como o carregamento de dados de um servidor, que pode demorar, devem ser feitas no método ngOnInit
.
ngOnDestroy
O método ngOnDestroy()
é chamado antes da remoção do componente. Nele, é possível liberar recursos utilizados que não são removidos automaticamente pelo garbage collector. Aqui, também se pode cancelar assinaturas de eventos do DOM, parar timers, etc.
ngOnChanges
O método ngOnChanges()
é chamado antes do método ngOnInit()
e sempre que as propriedades vinculadas sofrem alterações. Com o parâmetro SimpleChanges
, é possível obter o valor atual e o valor anterior da propriedade alterada. Por exemplo, considere o seguinte componente filho, ChildComponent
:
import { Component, Input, OnInit, OnChanges, SimpleChanges } from "@angular/core";
@Component({
selector: "child-comp",
standalone: true,
template: `<p>Olá {{ name }}</p>`,
})
export class ChildComponent implements OnInit, OnChanges {
@Input() name: string = "";
constructor() {
console.log("constructor");
}
ngOnInit() {
console.log("onInit");
}
ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) {
let change = changes[propName];
let current = JSON.stringify(change.currentValue);
let previous = JSON.stringify(change.previousValue);
console.log(`${propName}: currentValue = ${current}, previousValue = ${previous}`);
}
}
}
E suponha que este componente seja utilizado no componente principal AppComponent
:
import { Component, OnChanges, SimpleChanges } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { ChildComponent } from "./child.component";
@Component({
selector: "my-app",
standalone: true,
imports: [FormsModule, ChildComponent],
template: `<child-comp [name]="name"></child-comp>
<input type="text" [(ngModel)]="name" />
<input type="number" [(ngModel)]="age" />`,
})
export class AppComponent implements OnChanges {
name = "Tom";
age = 25;
ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) {
let change = changes[propName];
let current = JSON.stringify(change.currentValue);
let previous = JSON.stringify(change.previousValue);
console.log(`${propName}: currentValue = ${current}, previousValue = ${previous}`);
}
}
}
O valor da propriedade name
é passado para o componente filho ChildComponent
a partir do componente principal, AppComponent
. No entanto, o método ngOnChanges()
também é implementado no componente principal.
Se executarmos a aplicação, podemos observar que, a cada alteração da propriedade name no componente principal, o método ngOnChanges
é chamado:
É importante notar que este método é chamado apenas quando as propriedades de entrada com o decorador @Input
são alteradas. Portanto, a alteração da propriedade age
no AppComponent
não será monitorada aqui. Além disso, o método ngOnChanges()
é chamado quando há uma mudança nos inputs que o componente recebe de outro componente.
Implementação de Todos os Métodos
Vamos definir o seguinte componente filho:
import { Component, Input, OnInit, DoCheck, OnChanges, AfterContentInit, AfterContentChecked, AfterViewChecked, AfterViewInit } from "@angular/core";
@Component({
selector: "child-comp",
standalone: true,
template: `<p>Olá {{ name }}</p>`,
})
export class ChildComponent implements OnInit, DoCheck, OnChanges, AfterContentInit, AfterContentChecked, AfterViewChecked, AfterViewInit {
@Input() name: string = "";
count = 1;
ngOnInit() {
this.log(`ngOnInit`);
}
ngOnChanges() {
this.log(`OnChanges`);
}
ngDoCheck() {
this.log(`ngDoCheck`);
}
ngAfterViewInit() {
this.log(`ngAfterViewInit`);
}
ngAfterViewChecked() {
this.log(`ngAfterViewChecked`);
}
ngAfterContentInit() {
this.log(`ngAfterContentInit`);
}
ngAfterContentChecked() {
this.log(`ngAfterContentChecked`);
}
private log(msg: string) {
console.log(this.count + ". " + msg);
this.count++;
}
}
E vamos usar este componente no componente principal:
import { Component} from "@angular/core";
import { FormsModule } from "@angular/forms";
import { ChildComponent} from "./child.component";
@Component({
selector: "my-app",
standalone: true,
imports: [FormsModule, ChildComponent],
template: `<child-comp [name]="name"></child-comp>
<input type="text" [(ngModel)]="name" />`
})
export class AppComponent {
name = "Tom";
}