Conţinut
De ce avem nevoie de cârlige pentru ciclul de viață?
Cadrele front-end moderne mută aplicația de la stat la stat. Datele alimentează aceste actualizări. Aceste tehnologii interacționează cu datele care la rândul lor tranziționează starea. Cu fiecare schimbare de stare, există multe momente specifice în care anumite active devin disponibile.
La un moment dat, șablonul ar putea fi gata, în altul, datele vor fi finalizate. Codificarea pentru fiecare instanță necesită un mijloc de detectare. Cârligele ciclului de viață răspund acestei nevoi. Cadrele front-end moderne se împachetează cu o varietate de cârlige pentru ciclul de viață. Unghiular nu face excepție
Cârligele ciclului de viață explicate
Cârligele ciclului de viață sunt metode temporizate. Ele diferă când și de ce execută. Detectarea modificărilor declanșează aceste metode. Acestea se execută în funcție de condițiile ciclului curent. Rularea unghiulară detectează în mod constant modificările datelor sale. Cârligele ciclului de viață ajută la gestionarea efectelor sale.
Un aspect important al acestor cârlige este ordinea lor de execuție. Nu se abate niciodată. Acestea se execută pe baza unei serii predictibile de evenimente de încărcare produse dintr-un ciclu de detectare. Acest lucru le face previzibile.
Unele active sunt disponibile numai după executarea unui anumit cârlig. Desigur, un cârlig se execută numai în anumite condiții stabilite în ciclul curent de detectare a modificărilor.
Acest articol prezintă cârligele ciclului de viață în ordinea executării lor (dacă toate execută). Anumite condiții merită activarea unui cârlig. Există câțiva care execută o singură dată după inițializarea componentei.
Toate metodele ciclului de viață sunt disponibile de la @angular/core
. Deși nu este necesar, unghiular recomandă implementarea fiecărui cârlig. Această practică duce la mesaje de eroare mai bune cu privire la componentă.
Ordinul executării cârligelor ciclului de viață
ngOnChanges
ngOnChanges
declanșează în urma modificării @Input
membrii clasei legați. Date legate de @Input()
decoratorul provine dintr-o sursă externă. Când sursa externă modifică aceste date într-un mod detectabil, acestea trec prin @Input
proprietate din nou.
Cu această actualizare, ngOnChanges
trage imediat. De asemenea, se declanșează la inițializarea datelor de intrare. Cârligul primește un parametru opțional de tip SimpleChanges
. Această valoare conține informații despre proprietățile modificate legate de intrare.
import { Component, Input, OnChanges } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<h3>Child Component</h3>
<p>TICKS: {{ lifecycleTicks }}</p>
<p>DATA: {{ data }}</p>
`
})
export class ChildComponent implements OnChanges {
@Input() data: string;
lifecycleTicks: number = 0;
ngOnChanges() {
this.lifecycleTicks++;
}
}
@Component({
selector: 'app-parent',
template: `
<h1>ngOnChanges Example</h1>
<app-child [data]="arbitraryData"></app-child>
`
})
export class ParentComponent {
arbitraryData: string = 'initial';
constructor() {
setTimeout(() => {
this.arbitraryData="final";
}, 5000);
}
}
Rezumat: ParentComponent leagă datele de intrare de ChildComponent. Componenta primește aceste date prin @Input
proprietate. ngOnChanges
incendii. După cinci secunde, setTimeout
declanșatoare de apel invers. ParentComponent mută sursa de date a proprietății legate de intrare ChildComponent. Noile date circulă prin proprietatea de intrare. ngOnChanges
se aprinde din nou.
ngOnInit
ngOnInit
se declanșează o dată la inițializarea legăturii de intrare a unei componente (@Input
) proprietăți. Următorul exemplu va arăta similar cu ultimul. Cârligul nu se declanșează deoarece ChildComponent primește datele de intrare. Mai degrabă, se declanșează imediat după ce datele sunt redate în șablonul ChildComponent.
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<h3>Child Component</h3>
<p>TICKS: {{ lifecycleTicks }}</p>
<p>DATA: {{ data }}</p>
`
})
export class ChildComponent implements OnInit {
@Input() data: string;
lifecycleTicks: number = 0;
ngOnInit() {
this.lifecycleTicks++;
}
}
@Component({
selector: 'app-parent',
template: `
<h1>ngOnInit Example</h1>
<app-child [data]="arbitraryData"></app-child>
`
})
export class ParentComponent {
arbitraryData: string = 'initial';
constructor() {
setTimeout(() => {
this.arbitraryData="final";
}, 5000);
}
}
Rezumat: ParentComponent leagă datele de intrare de ChildComponent. ChildComponent primește aceste date prin intermediul său @Input
proprietate. Datele sunt redate șablonului. ngOnInit
incendii. După cinci secunde, setTimeout
declanșatoare de apel invers. ParentComponent mută sursa de date a proprietății legate de intrare ChildComponent. ngOnInit NU INCENDIAZĂ.
ngOnInit
este un cârlig unic. Inițializarea este singura sa preocupare.
ngDoCheck
ngDoCheck
se aprinde cu fiecare ciclu de detectare a modificărilor. Rularea unghiulară modifică frecvent detectarea. Efectuarea oricărei acțiuni va determina ciclul acesteia. ngDoCheck
se aprinde cu aceste cicluri. Folosiți-l cu precauție. Poate crea probleme de performanță atunci când este implementat incorect.
ngDoCheck
permite dezvoltatorilor să își verifice manual datele. Pot declanșa o nouă dată a aplicației în mod condiționat. În legătură cu ChangeDetectorRef
, dezvoltatorii își pot crea propriile verificări pentru detectarea modificărilor.
import { Component, DoCheck, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'app-example',
template: `
<h1>ngDoCheck Example</h1>
<p>DATA: {{ data[data.length - 1] }}</p>
`
})
export class ExampleComponent implements DoCheck {
lifecycleTicks: number = 0;
oldTheData: string;
data: string[] = ['initial'];
constructor(private changeDetector: ChangeDetectorRef) {
this.changeDetector.detach(); // lets the class perform its own change detection
setTimeout(() => {
this.oldTheData="final"; // intentional error
this.data.push('intermediate');
}, 3000);
setTimeout(() => {
this.data.push('final');
this.changeDetector.markForCheck();
}, 6000);
}
ngDoCheck() {
console.log(++this.lifecycleTicks);
if (this.data[this.data.length - 1] !== this.oldTheData) {
this.changeDetector.detectChanges();
}
}
}
Acordați atenție consolei față de ecran. Datele progresează până la „intermediare” înainte de înghețare. Trei runde de detectare a modificărilor au loc în această perioadă, așa cum este indicat în consolă. Încă o rundă de detectare a modificărilor are loc pe măsură ce „finalul” este împins până la sfârșitul anului this.data
. Apare apoi o ultimă rundă de detectare a modificărilor. Evaluarea declarației if determină că nu sunt necesare actualizări ale vizualizării.
Rezumat: Clasa instantaneează după două runde de detectare a modificărilor. Constructorul de clase inițiază setTimeout
de două ori. După trei secunde, prima setTimeout
declanșează detectarea modificărilor. ngDoCheck
marchează afișajul pentru o actualizare. Trei secunde mai târziu, a doua setTimeout
declanșează detectarea modificărilor. Nu sunt necesare actualizări de vizualizare în funcție de evaluarea ngDoCheck
.
Avertizare
Înainte de a continua, aflați diferența dintre conținutul DOM și vizualizarea DOM (DOM înseamnă Document Object Model).
Conținutul DOM definește HTML-ul interior al elementelor directive. În schimb, vizualizarea DOM este un șablon al unei componente, excluzând orice șablon HTML imbricat într-o directivă. Pentru o mai bună înțelegere, consultați această postare de blog.
ngAfterContentInit
ngAfterContentInit
se declanșează după inițializarea conținutului componentei DOM (se încarcă pentru prima dată). În așteptare @ContentChild(ren)
interogări este cazul de utilizare principal al cârligului.
@ContentChild(ren)
interogările dau referințe la elemente pentru conținutul DOM. Ca atare, acestea nu sunt disponibile decât după încărcarea conținutului DOM. De aici de ce ngAfterContentInit
și omologul său ngAfterContentChecked
sunt folosite.
import { Component, ContentChild, AfterContentInit, ElementRef, Renderer2 } from '@angular/core';
@Component({
selector: 'app-c',
template: `
<p>I am C.</p>
<p>Hello World!</p>
`
})
export class CComponent { }
@Component({
selector: 'app-b',
template: `
<p>I am B.</p>
<ng-content></ng-content>
`
})
export class BComponent implements AfterContentInit {
@ContentChild("BHeader", { read: ElementRef }) hRef: ElementRef;
@ContentChild(CComponent, { read: ElementRef }) cRef: ElementRef;
constructor(private renderer: Renderer2) { }
ngAfterContentInit() {
this.renderer.setStyle(this.hRef.nativeElement, 'background-color', 'yellow')
this.renderer.setStyle(this.cRef.nativeElement.children.item(0), 'background-color', 'pink');
this.renderer.setStyle(this.cRef.nativeElement.children.item(1), 'background-color', 'red');
}
}
@Component({
selector: 'app-a',
template: `
<h1>ngAfterContentInit Example</h1>
<p>I am A.</p>
<app-b>
<h3 #BHeader>BComponent Content DOM</h3>
<app-c></app-c>
</app-b>
`
})
export class AComponent { }
@ContentChild
rezultatele interogării sunt disponibile de la ngAfterContentInit
. Renderer2
actualizează conținutul DOM al BComponent care conține un h3
tag și componentă CC. Acesta este un exemplu obișnuit de proiecție de conținut.
Rezumat: Redarea începe cu AComponent. Pentru ca acesta să se termine, AComponent trebuie să redea BComponent. BComponent proiectează conținut imbricat în elementul său prin intermediul <ng-content></ng-content>
element. CComponent face parte din conținutul proiectat. Conținutul proiectat finalizează redarea. ngAfterContentInit
incendii. BComponent termină redarea. AComponent termină redarea. ngAfterContentInit
nu va mai trage.
ngAfterContentChecked
ngAfterContentChecked
se declanșează după fiecare ciclu de detectare a modificărilor care vizează conținutul DOM. Acest lucru permite dezvoltatorilor să faciliteze modul în care conținutul DOM reacționează la detectarea modificărilor. ngAfterContentChecked
poate declanșa frecvent și poate cauza probleme de performanță dacă este implementat prost.
ngAfterContentChecked
se declanșează și în timpul etapelor de inițializare a unei componente. Vine imediat după ngAfterContentInit
.
import { Component, ContentChild, AfterContentChecked, ElementRef, Renderer2 } from '@angular/core';
@Component({
selector: 'app-c',
template: `
<p>I am C.</p>
<p>Hello World!</p>
`
})
export class CComponent { }
@Component({
selector: 'app-b',
template: `
<p>I am B.</p>
<button (click)="$event">CLICK</button>
<ng-content></ng-content>
`
})
export class BComponent implements AfterContentChecked {
@ContentChild("BHeader", { read: ElementRef }) hRef: ElementRef;
@ContentChild(CComponent, { read: ElementRef }) cRef: ElementRef;
constructor(private renderer: Renderer2) { }
randomRGB(): string {
return `rgb(${Math.floor(Math.random() * 256)},
${Math.floor(Math.random() * 256)},
${Math.floor(Math.random() * 256)})`;
}
ngAfterContentChecked() {
this.renderer.setStyle(this.hRef.nativeElement, 'background-color', this.randomRGB());
this.renderer.setStyle(this.cRef.nativeElement.children.item(0), 'background-color', this.randomRGB());
this.renderer.setStyle(this.cRef.nativeElement.children.item(1), 'background-color', this.randomRGB());
}
}
@Component({
selector: 'app-a',
template: `
<h1>ngAfterContentChecked Example</h1>
<p>I am A.</p>
<app-b>
<h3 #BHeader>BComponent Content DOM</h3>
<app-c></app-c>
</app-b>
`
})
export class AComponent { }
Acest lucru diferă cu greu de ngAfterContentInit
. O simplă <button></button>
a fost adăugat la BComponent. Făcând clic pe acesta se produce o buclă de detectare a modificărilor. Acest lucru activează cârligul așa cum este indicat prin randomizarea lui background-color
.
Rezumat: Redarea începe cu AComponent. Pentru ca acesta să se termine, AComponent trebuie să redea BComponent. BComponent proiectează conținut imbricat în elementul său prin intermediul <ng-content></ng-content>
element. CComponent face parte din conținutul proiectat. Conținutul proiectat finalizează redarea. ngAfterContentChecked
incendii. BComponent termină redarea. AComponent termină redarea. ngAfterContentChecked
poate declanșa din nou prin detectarea modificărilor.
ngAfterViewInit
ngAfterViewInit
se declanșează o dată după terminarea inițializării DOM. Vizualizarea se încarcă întotdeauna imediat după conținut. ngAfterViewInit
așteaptă @ViewChild(ren)
interogări de rezolvat. Aceste elemente sunt interogate din aceeași vizualizare a componentei.
În exemplul de mai jos, BComponent’s h3
antetul este interogat. ngAfterViewInit
se execută imediat ce rezultatele interogării sunt disponibile.
import { Component, ViewChild, AfterViewInit, ElementRef, Renderer2 } from '@angular/core';
@Component({
selector: 'app-c',
template: `
<p>I am C.</p>
<p>Hello World!</p>
`
})
export class CComponent { }
@Component({
selector: 'app-b',
template: `
<p #BStatement>I am B.</p>
<ng-content></ng-content>
`
})
export class BComponent implements AfterViewInit {
@ViewChild("BStatement", { read: ElementRef }) pStmt: ElementRef;
constructor(private renderer: Renderer2) { }
ngAfterViewInit() {
this.renderer.setStyle(this.pStmt.nativeElement, 'background-color', 'yellow');
}
}
@Component({
selector: 'app-a',
template: `
<h1>ngAfterViewInit Example</h1>
<p>I am A.</p>
<app-b>
<h3>BComponent Content DOM</h3>
<app-c></app-c>
</app-b>
`
})
export class AComponent { }
Renderer2
modifică culoarea de fundal a antetului BComponent. Acest lucru indică faptul că elementul de vizualizare a fost interogat cu succes datorită ngAfterViewInit
.
Rezumat: Redarea începe cu AComponent. Pentru ca acesta să se termine, AComponent trebuie să redea BComponent. BComponent proiectează conținut imbricat în elementul său prin intermediul <ng-content></ng-content>
element. CComponent face parte din conținutul proiectat. Conținutul proiectat finalizează redarea. BComponent termină redarea. ngAfterViewInit
incendii. AComponent termină redarea. ngAfterViewInit
nu va mai trage.
ngAfterViewChecked
ngAfterViewChecked
se declanșează după orice ciclu de detectare a modificărilor care vizează vizualizarea componentei. ngAfterViewChecked
hook permite dezvoltatorilor să faciliteze modul în care detectarea modificărilor afectează DOM-ul de vizualizare.
import { Component, ViewChild, AfterViewChecked, ElementRef, Renderer2 } from '@angular/core';
@Component({
selector: 'app-c',
template: `
<p>I am C.</p>
<p>Hello World!</p>
`
})
export class CComponent { }
@Component({
selector: 'app-b',
template: `
<p #BStatement>I am B.</p>
<button (click)="$event">CLICK</button>
<ng-content></ng-content>
`
})
export class BComponent implements AfterViewChecked {
@ViewChild("BStatement", { read: ElementRef }) pStmt: ElementRef;
constructor(private renderer: Renderer2) { }
randomRGB(): string {
return `rgb(${Math.floor(Math.random() * 256)},
${Math.floor(Math.random() * 256)},
${Math.floor(Math.random() * 256)})`;
}
ngAfterViewChecked() {
this.renderer.setStyle(this.pStmt.nativeElement, 'background-color', this.randomRGB());
}
}
@Component({
selector: 'app-a',
template: `
<h1>ngAfterViewChecked Example</h1>
<p>I am A.</p>
<app-b>
<h3>BComponent Content DOM</h3>
<app-c></app-c>
</app-b>
`
})
export class AComponent { }
Rezumat: Redarea începe cu AComponent. Pentru ca acesta să se termine, AComponent trebuie să redea BComponent. BComponent proiectează conținut imbricat în elementul său prin intermediul <ng-content></ng-content>
element. CComponent face parte din conținutul proiectat. Conținutul proiectat finalizează redarea. BComponent termină redarea. ngAfterViewChecked
incendii. AComponent termină redarea. ngAfterViewChecked
poate declanșa din nou prin detectarea modificărilor.
Dând clic pe <button></button>
elementul inițiază o rundă de detectare a modificărilor. ngAfterContentChecked
trage și randomizează background-color
din elementele interogate, fiecare buton face clic.
ngOnDestroy
ngOnDestroy
se declanșează la eliminarea unei componente din vizualizare și DOM ulterioare. Acest cârlig oferă șansa de a curăța orice capete libere înainte de ștergerea unei componente.
import { Directive, Component, OnDestroy } from '@angular/core';
@Directive({
selector: '[appDestroyListener]'
})
export class DestroyListenerDirective implements OnDestroy {
ngOnDestroy() {
console.log("Goodbye World!");
}
}
@Component({
selector: 'app-example',
template: `
<h1>ngOnDestroy Example</h1>
<button (click)="toggleDestroy()">TOGGLE DESTROY</button>
<p appDestroyListener *ngIf="destroy">I can be destroyed!</p>
`
})
export class ExampleComponent {
destroy: boolean = true;
toggleDestroy() {
this.destroy = !this.destroy;
}
}
Rezumat: Se face clic pe buton. ExampleComponent destroy
membru comută fals. Directiva structurală *ngIf
evaluează la fals. ngOnDestroy
incendii. *ngIf
își înlătură gazda <p></p>
. Acest proces se repetă de câte ori faceți clic pe butonul pentru a comuta destroy
la fals.
Concluzie
Amintiți-vă că trebuie îndeplinite anumite condiții pentru fiecare cârlig. Vor executa întotdeauna în ordine una cu alta, indiferent. Acest lucru face ca cârligele să fie suficient de previzibile pentru a lucra, chiar dacă unele nu execută.
Cu cârligele ciclului de viață, sincronizarea execuției unei clase este ușoară. Acestea le permit dezvoltatorilor să urmărească unde are loc detectarea modificărilor și cum ar trebui să reacționeze aplicația. Acestea blochează codul care necesită dependențe bazate pe încărcare disponibile numai după o perioadă de timp.
Ciclul de viață al componentelor caracterizează cadrele front-end moderne. Angular își prezintă ciclul de viață oferind cârligele menționate mai sus.
Surse
- Echipa angulară. „Cârlige pentru ciclul de viață”. Google. Accesat la 2 iunie 2018
- Gechev, Minko. „ViewChildren and ContentChildren in Angular”. Accesat la 2 iunie 2018
Resurse
#Cârlige #ciclu #viață #unghiular #ngOnChanges #ngOnInit #și #multe #altele
Cârlige de ciclu de viață unghiular: ngOnChanges, ngOnInit și multe altele