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

ngOnChangesudløser efter ændringen af @Inputbundne 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 @Inputejendommen igen.

Med denne opdatering fyres ngOnChangesstraks. 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@Inputejendom. ngOnChangesbrande. Efter fem sekundersetTimeoutudløses tilbagekaldet. ParentComponent muterer datakilden til ChildComponents input-bundne egenskab. De nye data flyder gennem inputegenskaben. ngOnChangesbrænder endnu en gang.

ngOnInit

ngOnInitudlø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@Inputejendom. Dataene gengives til skabelonen. ngOnInitbrande. Efter fem sekundersetTimeoutudløses tilbagekaldet. ParentComponent muterer datakilden til ChildComponents input-bundne egenskab. ngOnInit BRANDER IKKE .

ngOnIniter en en-og-færdig krog. Initialisering er dens eneste bekymring.

ngDoCheck

ngDoCheckbrænder med hver cyklus til detektering af ændringer. Vinkelforløb skifter ofte detektion. Hvis du udfører en handling, får den cyklus. ngDoCheckbrænder med disse cyklusser. Brug det med forsigtighed. Det kan skabe præstationsproblemer, når det implementeres forkert.

ngDoChecklader udviklere kontrollere deres data manuelt. De kan udløse en ny ansøgningsdato betinget. I forbindelse med ChangeDetectorRefkan 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 startersetTimeoutto gange. Efter tre sekundersetTimeoutudløserden førstedetektion af ændring. ngDoCheckmarkerer displayet for en opdatering. Tre sekunder seneresetTimeoutudlø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

ngAfterContentInitudlø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 ngAfterContentInitog 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 @ContentChildforespørgsel resultater er tilgængelige fra ngAfterContentInit. Renderer2opdaterer indholdet DOM af BComponent indeholdende et h3tag 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