Ъглови куки за жизнения цикъл: ngOnChanges, ngOnInit и др

Защо се нуждаем от куки за жизнения цикъл?

Съвременните интерфейсни рамки преместват приложението от състояние в състояние. Данните подхранват тези актуализации. Тези технологии взаимодействат с данните, които от своя страна превръщат състоянието. С всяка промяна на държавата има много специфични моменти, в които определени активи стават достъпни.

В един случай шаблонът може да е готов, а в друг данните ще приключат с качването. Кодирането за всеки екземпляр изисква средства за откриване. Куките за жизнения цикъл отговарят на тази нужда. Модерните предни рамки се опаковат с разнообразни куки за жизнения цикъл. Ъгловата не е изключение

Обяснени куки за жизнения цикъл

Куките на жизнения цикъл са методи с време. Те се различават по това кога и защо изпълняват. Откриването на промени задейства тези методи. Те се изпълняват в зависимост от условията на текущия цикъл. Ъгловите изпълнения постоянно откриват промени в данните си. Куките за жизнения цикъл помагат за управлението на неговите ефекти.

Важен аспект на тези куки е техният ред на изпълнение. Никога не се отклонява. Те се изпълняват въз основа на предвидима поредица от събития на натоварване, произведени от цикъл на откриване. Това ги прави предсказуеми.

Някои активи са достъпни само след изпълнение на определена кука. Разбира се, куката се изпълнява само при определени условия, зададени в текущия цикъл на откриване на промени.

Тази статия представя куките на жизнения цикъл по реда на тяхното изпълнение (ако всички те се изпълняват). Някои условия заслужават активиране на куката. Има няколко, които изпълняват само веднъж след инициализация на компонента.

Всички методи на жизнения цикъл са достъпни от @angular/core. Въпреки че не се изисква, Angular препоръчва да се приложи всяка кука. Тази практика води до по-добри съобщения за грешки по отношение на компонента.

Поръчка за изпълнение на куките на жизнения цикъл

ngOnChanges

ngOnChangesзадейства след модификацията на @Inputобвързани членове на класа. Данните, обвързани от @Input()декоратора, идват от външен източник. Когато външният източник променя тези данни по откриваем начин, той отново преминава през @Inputсвойството.

С тази актуализация ngOnChangesведнага се задейства. Той също така се задейства при инициализация на входни данни. Куката получава един незадължителен параметър от тип SimpleChanges. Тази стойност съдържа информация за променените свойства, свързани с входа.

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); } }

Резюме: ParentComponent обвързва входните данни с ChildComponent. Компонентът получава тези данни чрез своето@Inputсвойство. ngOnChangesпожари. След пет секундиsetTimeoutзадейства обратното повикване. ParentComponent мутира източника на данни на свойството ChildComponent, свързано с входа. Новите данни преминават през входното свойство. ngOnChangesпожари отново.

ngOnInit

ngOnInitзадейства се веднъж при инициализация на @Inputсвойствата на компонента, свързани с ( ). Следващият пример ще изглежда подобно на последния. Куката не се задейства, тъй като ChildComponent получава входните данни. По-скоро тя се задейства веднага след като данните се представят в шаблона ChildComponent.

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); } }

Резюме: ParentComponent обвързва входните данни с ChildComponent. ChildComponent получава тези данни чрез своето@Inputсвойство. Данните се показват в шаблона. ngOnInitпожари. След пет секундиsetTimeoutзадейства обратното повикване. ParentComponent мутира източника на данни на свойството ChildComponent, свързано с входа. ngOnInit НЕ ПУСКА.

ngOnInitе еднократна кука. Инициализацията е единствената му грижа.

ngDoCheck

ngDoCheckпожари с всеки цикъл за откриване на промени. Откриването на промени в ъгловите пробези често се променя. Извършването на каквото и да е действие ще доведе до неговия цикъл. ngDoCheckпожари с тези цикли. Използвайте го с повишено внимание. Той може да създаде проблеми с производителността, когато е внедрен неправилно.

ngDoCheckпозволява на разработчиците да проверяват данните си ръчно. Те могат да задействат нова дата за кандидатстване условно. Във връзка с това ChangeDetectorRef, разработчиците могат да създават свои собствени чекове за откриване на промени.

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(); } } }

Обърнете внимание на конзолата спрямо дисплея. Данните напредват до „междинен“ преди замразяване. През този период се случват три кръга на откриване на промени, както е посочено в конзолата. Още един кръг на откриване на промяната се случва, когато „окончателният“ бъде изместен в края на this.data. След това настъпва един последен кръг на откриване на промяна. Оценката на оператора if определя, че не са необходими актуализации на изгледа.

Резюме: Класът създава екземпляри след два кръга на откриване на промени. Конструкторът на клас инициираsetTimeoutдва пъти. След три секунди първитеsetTimeoutзадействания променят откриването. ngDoCheckмаркира дисплея за актуализация. Три секунди по-късно вториятsetTimeoutзадейства промяна на откриването. Не са необходими актуализации на прегледа според оценката наngDoCheck.

Внимание

Преди да продължите, научете разликата между съдържанието DOM и изгледа DOM (DOM означава Document Object Model).

Съдържанието DOM дефинира innerHTML на елементите на директивите. И обратно, изгледът DOM е шаблон на компонент, с изключение на всеки HTML шаблон, вложен в директива. За по-добро разбиране вижте тази публикация в блога.

ngAfterContentInit

ngAfterContentInitсе задейства, след като съдържанието на компонента DOM се инициализира (зарежда се за първи път). Изчакването на @ContentChild(ren)заявки е основният случай на употреба на куката.

@ContentChild(ren)заявките дават референции на елементи за съдържанието DOM. Като такива те са достъпни едва след зареждане на съдържанието DOM. Следователно защо ngAfterContentInitи неговият аналог 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 { }

Резултатите от @ContentChildзаявката са достъпни от ngAfterContentInit. Renderer2актуализира съдържанието DOM на BComponent, съдържащо h3маркер и CComponent. Това е често срещан пример за прожектиране на съдържание.

Резюме: Рендирането започва с AComponent. За да завърши, AComponent трябва да изобрази BComponent. BComponent проектира съдържание, вложено в неговия елемент чрезелемента. CComponent е част от проектираното съдържание. Прожектираното съдържание завършва рендирането. ngAfterContentInitпожари. BComponent завършва изобразяването. AComponent завършва изобразяването. ngAfterContentInitняма да стреля отново.

ngAfterContentChecked

ngAfterContentCheckedзадейства се след всеки цикъл на откриване на промени, насочени към съдържанието DOM. Това позволява на разработчиците да улеснят реакцията на DOM на съдържанието при откриване на промени. ngAfterContentCheckedможе да задейства често и да причини проблеми с производителността, ако е лошо внедрена

ngAfterContentCheckedзадейства се и по време на етапите на инициализация на компонент. Идва веднага след това 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 { }

Това едва ли се различава от ngAfterContentInit. Просто беше добавено към BComponent. Кликването му води до контур за откриване на промяна. Това активира куката, както е посочено от рандомизирането на background-color.

Резюме: Рендирането започва с AComponent. За да завърши, AComponent трябва да изобрази BComponent. BComponent проектира съдържание, вложено в неговия елемент чрезелемента. CComponent е част от проектираното съдържание. Прожектираното съдържание завършва рендирането. ngAfterContentCheckedпожари. BComponent завършва изобразяването. AComponent завършва изобразяването. ngAfterContentCheckedможе да се задейства отново чрез откриване на промени.

ngAfterViewInit

ngAfterViewInitзадейства се веднъж, след като изгледът DOM приключи инициализирането. Изгледът винаги се зарежда веднага след съдържанието. ngAfterViewInitчака @ViewChild(ren)за разрешаване на заявки. Тези елементи се заявяват от един и същ изглед на компонента.

В примера по-долу h3заглавката на BComponent е заявена. ngAfterViewInitизпълнява се веднага щом са налични резултатите от заявката.

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променя цвета на фона на заглавката на BComponent. Това показва, че елементът за изглед е бил успешно заявен благодарение на 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.

The component lifecycle characterizes modern front end frameworks. Angular lays out its lifecycle by providing the aforementioned hooks.

Sources

  • Angular Team. “Lifecycle Hooks”. Google. Accessed 2 June 2018
  • Gechev, Minko. “ViewChildren and ContentChildren in Angular”. Accessed 2 June 2018

Resources

  • Angular Documentation
  • Angular GitHub Repository
  • Lifecycle Hooks in Depth