Vinklede livscykluskroge: ngOnChanges, ngOnInit og mere
Hvorfor har vi brug for kroge til livscyklus?
Moderne frontend-rammer flytter applikationen fra stat til stat. Data fremmer disse opdateringer. Disse teknologier interagerer med dataene, som igen overgår staten. Med hver statsændring er der mange specifikke øjeblikke, hvor visse aktiver bliver tilgængelige.
På et tidspunkt kan skabelonen være klar, i en anden data er uploadet færdig. Kodning for hver forekomst kræver et middel til detektion. Lifecycle kroge svarer på dette behov. Moderne frontend-rammer pakker sig med en række livscykluskroge. Vinklet er ingen undtagelse
Livscykluskroge forklaret
Lifecycle kroge er tidsbestemte metoder. De adskiller sig i hvornår og hvorfor de udfører. Ændringsdetektering udløser disse metoder. De udføres afhængigt af forholdene i den aktuelle cyklus. Vinkelforløb ændrer konstant detektion på sine data. Lifecycle kroge hjælper med at styre dens effekter.
Et vigtigt aspekt af disse kroge er deres rækkefølge. Det afviger aldrig. De udfører baseret på en forudsigelig serie af belastningshændelser produceret fra en detektionscyklus. Dette gør dem forudsigelige.
Nogle aktiver er kun tilgængelige, når en bestemt krog er udført. Selvfølgelig udfører en krog kun under visse betingelser, der er indstillet i den aktuelle detektionscyklus for ændringer.
Denne artikel præsenterer livscykluskrogene i rækkefølge efter deres udførelse (hvis de alle udfører). Visse betingelser fortjener en krogs aktivering. Der er nogle få, der kun udfører en gang efter komponentinitialisering.
Alle livscyklusmetoder er tilgængelige fra @angular/core
. Selvom det ikke kræves, anbefaler Angular at implementere alle kroge. Denne praksis fører til bedre fejlmeddelelser vedrørende komponenten.
Orden af eksekvering af livscykluskroge
ngOnChanges
ngOnChanges
udløser efter ændringen af @Input
bundne klassemedlemmer. Data bundet af @Input()
dekoratøren kommer fra en ekstern kilde. Når den eksterne kilde ændrer disse data på en påviselig måde, passerer den gennem @Input
ejendommen igen.
Med denne opdatering fyres ngOnChanges
straks. Det affyres også ved initialisering af inputdata. Krogen modtager en valgfri parameter af typen SimpleChanges
. Denne værdi indeholder oplysninger om de ændrede input-bundne egenskaber.
import { Component, Input, OnChanges } from '@angular/core'; @Component({ selector: 'app-child', template: ` Child Component
TICKS: {{ lifecycleTicks }}
DATA: {{ data }}
` }) export class ChildComponent implements OnChanges { @Input() data: string; lifecycleTicks: number = 0; ngOnChanges() { this.lifecycleTicks++; } } @Component({ selector: 'app-parent', template: ` ngOnChanges Example
` }) export class ParentComponent { arbitraryData: string = 'initial'; constructor() { setTimeout(() => { this.arbitraryData = 'final'; }, 5000); } }
Resumé: ParentComponent binder inputdata til ChildComponent. Komponenten modtager disse data gennem sin@Input
ejendom. ngOnChanges
brande. Efter fem sekundersetTimeout
udløses tilbagekaldet. ParentComponent muterer datakilden til ChildComponents input-bundne egenskab. De nye data flyder gennem inputegenskaben. ngOnChanges
brænder endnu en gang.
ngOnInit
ngOnInit
udløses en gang ved initialisering af en komponents input-bundne ( @Input
) egenskaber. Det næste eksempel vil ligne det sidste. Krogen tændes ikke, da ChildComponent modtager inputdataene. Snarere affyres det lige efter, at dataene gengives til ChildComponent-skabelonen.
import { Component, Input, OnInit } from '@angular/core'; @Component({ selector: 'app-child', template: ` Child Component
TICKS: {{ lifecycleTicks }}
DATA: {{ data }}
` }) export class ChildComponent implements OnInit { @Input() data: string; lifecycleTicks: number = 0; ngOnInit() { this.lifecycleTicks++; } } @Component({ selector: 'app-parent', template: ` ngOnInit Example
` }) export class ParentComponent { arbitraryData: string = 'initial'; constructor() { setTimeout(() => { this.arbitraryData = 'final'; }, 5000); } }
Resumé: ParentComponent binder inputdata til ChildComponent. ChildComponent modtager disse data gennem sin@Input
ejendom. Dataene gengives til skabelonen. ngOnInit
brande. Efter fem sekundersetTimeout
udløses tilbagekaldet. ParentComponent muterer datakilden til ChildComponents input-bundne egenskab. ngOnInit BRANDER IKKE .
ngOnInit
er en en-og-færdig krog. Initialisering er dens eneste bekymring.
ngDoCheck
ngDoCheck
brænder med hver cyklus til detektering af ændringer. Vinkelforløb skifter ofte detektion. Hvis du udfører en handling, får den cyklus. ngDoCheck
brænder med disse cyklusser. Brug det med forsigtighed. Det kan skabe præstationsproblemer, når det implementeres forkert.
ngDoCheck
lader udviklere kontrollere deres data manuelt. De kan udløse en ny ansøgningsdato betinget. I forbindelse med ChangeDetectorRef
kan udviklere oprette deres egne kontroller for detektering af ændringer.
import { Component, DoCheck, ChangeDetectorRef } from '@angular/core'; @Component({ selector: 'app-example', template: ` ngDoCheck Example
DATA: {{ data[data.length - 1] }}
` }) 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(); } } }
Vær opmærksom på konsollen versus skærmen. Dataene skrider frem til 'mellemliggende' inden frysning. Tre runder med registrering af ændringer finder sted i løbet af denne periode som angivet i konsollen. En yderligere runde med detektering af ændringer finder sted, når 'endelig' bliver skubbet til slutningen af this.data
. En sidste runde med detektering af ændringer finder sted. Evalueringen af if-sætningen bestemmer, at der ikke er behov for opdateringer af visningen.
Resumé: Klassen instantieres efter to runder med registrering af ændringer. Klassekonstruktør startersetTimeout
to gange. Efter tre sekundersetTimeout
udløserden førstedetektion af ændring. ngDoCheck
markerer displayet for en opdatering. Tre sekunder seneresetTimeout
udløserdet andetændringsdetektering. Ingen visningsopdateringer nødvendige i henhold til evalueringen afngDoCheck
.
Advarsel
Inden du fortsætter, skal du lære forskellen mellem indholdet DOM og se DOM (DOM står for Document Object Model).
Indholdet DOM definerer den indre HTML af direktivelementer. Omvendt er visningen DOM en komponents skabelon, eksklusive enhver skabelon-HTML, der er indlejret i et direktiv. For en bedre forståelse henvises til dette blogindlæg.
ngAfterContentInit
ngAfterContentInit
udløses efter komponentens indhold DOM initialiseres (indlæses for første gang). At vente på @ContentChild(ren)
forespørgsler er krogens primære brugssag.
@ContentChild(ren)
forespørgsler giver elementreferencer for indholdet DOM. Som sådan er de ikke tilgængelige før efter indholdet af DOM. Derfor bruges hvorfor ngAfterContentInit
og dets modstykke ngAfterContentChecked
.
import { Component, ContentChild, AfterContentInit, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-c', template: ` I am C.
Hello World!
` }) export class CComponent { } @Component({ selector: 'app-b', template: ` I am B.
` }) 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: ` ngAfterContentInit Example
I am A.
BComponent Content DOM
` }) export class AComponent { }
De @ContentChild
forespørgsel resultater er tilgængelige fra ngAfterContentInit
. Renderer2
opdaterer indholdet DOM af BComponent indeholdende et h3
tag og CComponent. Dette er et almindeligt eksempel på indholdsprojektion.
Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the element. CComponent is part of the projected content. The projected content finishes rendering.
ngAfterContentInit
fires. BComponent finishes rendering. AComponent finishes rendering. ngAfterContentInit
will not fire again.
ngAfterContentChecked
ngAfterContentChecked
fires after every cycle of change detection targeting the content DOM. This lets developers facilitate how the content DOM reacts to change detection. ngAfterContentChecked
can fire frequently and cause performance issues if poorly implemented.
ngAfterContentChecked
fires during a component’s initialization stages too. It comes right after ngAfterContentInit
.
import { Component, ContentChild, AfterContentChecked, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-c', template: ` I am C.
Hello World!
` }) export class CComponent { } @Component({ selector: 'app-b', template: ` I am B.
CLICK ` }) 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: ` ngAfterContentChecked Example
I am A.
BComponent Content DOM
` }) export class AComponent { }
This hardly differs from ngAfterContentInit
. A mere was added to BComponent. Clicking it causes a change detection loop. This activates the hook as indicated by the randomization of
background-color
.
Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the element. CComponent is part of the projected content. The projected content finishes rendering.
ngAfterContentChecked
fires. BComponent finishes rendering. AComponent finishes rendering. ngAfterContentChecked
may fire again through change detection.
ngAfterViewInit
ngAfterViewInit
fires once after the view DOM finishes initializing. The view always loads right after the content. ngAfterViewInit
waits on @ViewChild(ren)
queries to resolve. These elements are queried from within the same view of the component.
In the example below, BComponent’s h3
header is queried. ngAfterViewInit
executes as soon as the query’s results are available.
import { Component, ViewChild, AfterViewInit, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-c', template: ` I am C.
Hello World!
` }) export class CComponent { } @Component({ selector: 'app-b', template: ` I am B.
` }) 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: ` ngAfterViewInit Example
I am A.
BComponent Content DOM
` }) export class AComponent { }
Renderer2
changes the background color of BComponent’s header. This indicates the view element was successfully queried thanks to ngAfterViewInit
.
Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the element. CComponent is part of the projected content. The projected content finishes rendering. BComponent finishes rendering.
ngAfterViewInit
fires. AComponent finishes rendering. ngAfterViewInit
will not fire again.
ngAfterViewChecked
ngAfterViewChecked
fires after any change detection cycle targeting the component’s view. The ngAfterViewChecked
hook lets developers facilitate how change detection affects the view DOM.
import { Component, ViewChild, AfterViewChecked, ElementRef, Renderer2 } from '@angular/core'; @Component({ selector: 'app-c', template: ` I am C.
Hello World!
` }) export class CComponent { } @Component({ selector: 'app-b', template: ` I am B.
CLICK ` }) 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: ` ngAfterViewChecked Example
I am A.
BComponent Content DOM
` }) export class AComponent { }
Summary: Rendering starts with AComponent. For it to finish, AComponent must render BComponent. BComponent projects content nested in its element through the element. CComponent is part of the projected content. The projected content finishes rendering. BComponent finishes rendering.
ngAfterViewChecked
fires. AComponent finishes rendering. ngAfterViewChecked
may fire again through change detection.
Clicking the element initiates a round of change detection.
ngAfterContentChecked
fires and randomizes the background-color
of the queried elements each button click.
ngOnDestroy
ngOnDestroy
fires upon a component’s removal from the view and subsequent DOM. This hook provides a chance to clean up any loose ends before a component’s deletion.
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: ` ngOnDestroy Example
TOGGLE DESTROY I can be destroyed!
` }) export class ExampleComponent { destroy: boolean = true; toggleDestroy() { this.destroy = !this.destroy; } }
Summary: The button is clicked. ExampleComponent’s destroy
member toggles false. The structural directive *ngIf
evaluates to false. ngOnDestroy
fires. *ngIf
removes its host . This process repeats any number of times clicking the button to toggle
destroy
to false.
Conclusion
Remember that certain conditions must be met for each hook. They will always execute in order of each other regardless. This makes hooks predictable enough to work with even if some do not execute.
With lifecycle hooks, timing the execution of a class is easy. They let developers track where change detection is occurring and how the application should react. They stall for code that requires load-based dependencies available only after sometime.
Komponentens livscyklus karakteriserer moderne frontend-rammer. Vinkel angiver sin livscyklus ved at tilvejebringe de førnævnte kroge.
Kilder
- Vinkelhold. “Lifecycle Hooks”. Google . Adgang til 2. juni 2018
- Gechev, Minko. “ViewChildren and ContentChildren in Angular”. Adgang til 2. juni 2018
Ressourcer
- Vinklet dokumentation
- Vinklet GitHub-lager
- Livscyklus kroge i dybde