5. Lifecycle Hooks (Number to be updated)
Lifecycle sequence:
constructor(): ...
ngOnChanges(): Responds when Angular (re)-sets data bound input properties.Method receives a SimpleChange object of current and previous property values.
ngOnInit(): Initialize directive/component after Angular first displays data bound properties and sets directive component's input properties.
ngDoCheck(): Detect and act upon changes Angular can't or won't detect on its own.
ngAfterContentInit(): Responds after Angular projects external content into the component's view.
ngAfterContentChecked(): Responds after Angular checks the content projected into the component.
ngAfterViewInit(): Responds after Angular initializes the component's views and child views.
ngAfterViewChecked(): Responds after Angular checks the component's views and child views.
ngOnDestroy(): Cleanup just before Angular destroys the directive/component. Unsubscribe Observables and detach event handlers to avoid memory leaks.
Interfaces are optional(technically):
Good practice to implement them, same as the @Injectable decorator. Make your code more readable and maintainable.
LoggerService:
import { Injectable } from '@angular/core';
@Injectable()
export class LoggerService {
logs: string[] = [];
previousMessage = '';
previousMessageCount = 1;
log(message: string) {
if (message === this.previousMessage) {
// repeat message; update last log entry with count.
this.logs[this.logs.length -1] = message + `(${this.previousMessageCount += 1}x)`;
} else {
// new message; log it.
this.previousMessage = message;
this.previousMessageCount = 1;
this.logs.push(message);
}
}
clear() { this.logs.length = 0; }
// schedules a view refresh to ensure display catches up
tick() { this.tick_then(() => { }); }
tick_then(fn: () => any) { setTimeout(fn, 0); }
}
MyTechnologyComponent:
import { Component, Input} from '@angular/core';
import { OnInit, OnChanges, SimpleChanges, DoCheck, AfterContentInit, AfterContentChecked,
AfterViewInit, AfterViewChecked, OnDestroy} from '@angular/core';
import { LoggerService } from './logger.service';
let nextId = 1;
export class MyTechnology implements OnInit {
constructor(private logger: LoggerService) { }
// implements OnInit ngOnInit method
ngOnInit() { this.logIt('OnInit'); }
logIt(message: string): void {
this.logger.log(`#${nextId++} ${message}`);
}
}
@Component({
selector: 'app-my-technology',
template: `<p>Now you see my technology: {{ name }}</p>`,
styles: [`p {background: #ff8080; padding: 10px; }`]
})
export class MyTechnologyComponent extends MyTechnology implements OnInit, OnChanges,
DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit,
AfterViewChecked, OnDestroy {
@Input() name: string;
private verb = 'initialized';
constructor(logger: LoggerService) {
super(logger);
}
// only called for/if there is an @input variable set by the parent.
ngOnChanges(changes: SimpleChanges) {
const changesMessages: string[] = [];
for (const propertyName in changes) {
if (propertyName === 'name') {
const name = changes['name'].currentValue;
changesMessages.push(`name ${this.verb} to "${name}"`);
} else {
changesMessages.push(propertyName + ' ' + this.verb);
}
}
this.logIt(`OnChanges: ${changesMessages.join('; ')}`);
this.verb = 'changed'; // next time it will be a change
}
// beware called frequently!
// called in every change detection cycle anywhere on the page
ngDoCheck() { this.logIt(`DoCheck`); }
ngAfterContentInit() { this.logIt(`AfterContentInit`); }
// beware called frequently!
// called in every change detection cycle anywhere on the page
ngAfterContentChecked() { this.logIt(`AfterContentChecked`); }
ngAfterViewInit() { this.logIt(`AfterViewInit`); }
// called in every change detection cycle anywhere on the page
ngAfterViewChecked() { this.logIt(`AfterViewChecked`); }
ngOnDestroy() { this.logIt(`OnDestroy`); }
}
MyTechnologyHostComponent:
import { Component } from '@angular/core';
import { LoggerService } from './logger.service';
@Component({
selector: 'app-my-technology-host',
template: `
<div class="host">
<h2>My-Technology-Host</h2>
<button class="redy" (click)="toggle()">
{{ hasChild ? 'Destroy' : 'Create' }} MyTechnologyComponent
</button>
<button class="redy" (click)="updateTechnology()" [hidden]="!hasChild">Update Technology</button>
<app-my-technology *ngIf="hasChild" [name]="technologyName"></app-my-technology>
<h4>--- Lifecycle Hook Log ---</h4>
<div *ngFor="let message of hookLog">{{ message }}</div>
</div>
`,
styles: [`.host { background: #ff3333; color: white; } .redy { background: #4d0000; }`],
providers: [ LoggerService ]
})
export class MyTechnologyHostComponent {
hasChild = false;
hookLog: string[] = [];
technologyName = 'Angular 4';
private logger: LoggerService;
constructor(logger: LoggerService) {
this.logger = logger;
this.hookLog = logger.logs;
}
toggle() {
this.hasChild = !this.hasChild;
if (this.hasChild) {
this.technologyName = 'Angular 4';
this.logger.clear();
}
this.logger.tick();
}
updateTechnology() {
this.technologyName += '*';
this.logger.tick();
}
}
Let's have a look at this beauty in our Chromium Web Browser. Note, My Polymer Princess is for my eyes only: