parent
82383bc673
commit
1a98ef5844
|
@ -0,0 +1 @@
|
|||
**/*.js
|
|
@ -0,0 +1,102 @@
|
|||
// #docregion
|
||||
import {
|
||||
Component, Input, Output,
|
||||
AfterContentChecked, AfterContentInit, ContentChild,
|
||||
AfterViewInit, ViewChild
|
||||
} from 'angular2/core';
|
||||
|
||||
import {ChildComponent} from './child.component';
|
||||
import {LoggerService} from './logger.service';
|
||||
|
||||
@Component({
|
||||
selector: 'after-content',
|
||||
template: `
|
||||
<div class="after-content">
|
||||
<div>-- child content begins --</div>
|
||||
|
||||
<ng-content></ng-content>
|
||||
|
||||
<div>-- child content ends --</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.after-content {background: LightCyan; padding: 8px;}'],
|
||||
|
||||
})
|
||||
export class AfterContentComponent
|
||||
implements AfterContentChecked, AfterContentInit, AfterViewInit {
|
||||
|
||||
private _logger:LoggerService;
|
||||
|
||||
constructor(logger:LoggerService){
|
||||
this._logger = logger;
|
||||
logger.log('AfterContent ctor: ' + this._getMessage());
|
||||
}
|
||||
|
||||
// Query for a CONTENT child of type `ChildComponent`
|
||||
@ContentChild(ChildComponent) contentChild: ChildComponent;
|
||||
|
||||
// Query for a VIEW child of type`ChildComponent`
|
||||
// No such VIEW child exists!
|
||||
// This component holds content but no view of that type.
|
||||
@ViewChild(ChildComponent) viewChild: ChildComponent;
|
||||
|
||||
|
||||
///// Hooks
|
||||
ngAfterContentInit() {
|
||||
// contentChild is set after the content has been initialized
|
||||
this._logger.log('AfterContentInit: ' + this._getMessage());
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this._logger.log(`AfterViewInit: There is ${this.viewChild ? 'a' : 'no'} view child`);
|
||||
}
|
||||
|
||||
private _prevHero:string;
|
||||
ngAfterContentChecked() {
|
||||
// contentChild is updated after the content has been checked
|
||||
// Called frequently; only report when the hero changes
|
||||
if (!this.contentChild || this._prevHero === this.contentChild.hero) {return;}
|
||||
this._prevHero = this.contentChild.hero;
|
||||
this._logger.log('AfterContentChecked: ' + this._getMessage());
|
||||
}
|
||||
|
||||
private _getMessage(): string {
|
||||
let cmp = this.contentChild;
|
||||
return cmp ? `"${cmp.hero}" child content` : 'no child content';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
|
||||
@Component({
|
||||
selector: 'after-content-parent',
|
||||
template: `
|
||||
<div class="parent">
|
||||
<h2>AfterContent</h2>
|
||||
|
||||
<after-content>
|
||||
<input [(ngModel)]="hero">
|
||||
<button (click)="showChild = !showChild">Toggle child view</button>
|
||||
|
||||
<my-child *ngIf="showChild" [hero]="hero"></my-child>
|
||||
</after-content>
|
||||
|
||||
<h4>-- Lifecycle Hook Log --</h4>
|
||||
<div *ngFor="#msg of hookLog">{{msg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.parent {background: powderblue; padding: 8px; margin:100px 8px;}'],
|
||||
directives: [AfterContentComponent, ChildComponent],
|
||||
providers:[LoggerService]
|
||||
})
|
||||
export class AfterContentParentComponent {
|
||||
|
||||
hookLog:string[];
|
||||
hero = 'Magneta';
|
||||
showChild = true;
|
||||
|
||||
constructor(logger:LoggerService){
|
||||
this.hookLog = logger.logs;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// #docregion
|
||||
import {
|
||||
Component, Input, Output,
|
||||
AfterContentInit, ContentChild,
|
||||
AfterViewChecked, AfterViewInit, ViewChild
|
||||
} from 'angular2/core';
|
||||
|
||||
import {ChildComponent} from './child.component';
|
||||
import {LoggerService} from './logger.service';
|
||||
|
||||
@Component({
|
||||
selector: 'after-view-parent',
|
||||
template: `
|
||||
<div class="parent">
|
||||
<h2>AfterView</h2>
|
||||
|
||||
<div>
|
||||
<input [(ngModel)]="hero">
|
||||
<button (click)="showChild = !showChild">Toggle child view</button>
|
||||
|
||||
<my-child *ngIf="showChild" [hero]="hero"></my-child>
|
||||
</div>
|
||||
|
||||
<h4>-- Lifecycle Hook Log --</h4>
|
||||
<div *ngFor="#msg of hookLog">{{msg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.parent {background: burlywood; padding: 8px; margin:100px 8px;}'],
|
||||
directives: [ChildComponent],
|
||||
providers:[LoggerService]
|
||||
})
|
||||
export class AfterViewParentComponent
|
||||
implements AfterContentInit, AfterViewChecked, AfterViewInit {
|
||||
|
||||
private _logger:LoggerService;
|
||||
|
||||
constructor(logger:LoggerService){
|
||||
this._logger = logger;
|
||||
this.hookLog = logger.logs;
|
||||
logger.log('AfterView ctor: ' + this._getMessage());
|
||||
}
|
||||
|
||||
hookLog:string[];
|
||||
hero = 'Magneta';
|
||||
showChild = true;
|
||||
|
||||
// Query for a CONTENT child of type `ChildComponent`
|
||||
// No such CONTENT child exists!
|
||||
// This component holds a view but no content of that type.
|
||||
@ContentChild(ChildComponent) contentChild: ChildComponent;
|
||||
|
||||
// Query for a VIEW child of type `ChildComponent`
|
||||
@ViewChild(ChildComponent) viewChild: ChildComponent;
|
||||
|
||||
|
||||
///// Hooks
|
||||
ngAfterContentInit() {
|
||||
this._logger.log(`AfterContentInit: There is ${this.contentChild ? 'a' : 'no'} content child`);
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
// viewChild is set after the view has been initialized
|
||||
this._logger.log('AfterViewInit: ' + this._getMessage());
|
||||
}
|
||||
|
||||
private _prevHero:string;
|
||||
ngAfterViewChecked() {
|
||||
// viewChild is updated after the view has been checked
|
||||
// Called frequently; only report when the hero changes
|
||||
if (!this.viewChild || this._prevHero === this.viewChild.hero) {return;}
|
||||
this._prevHero = this.viewChild.hero;
|
||||
this._logger.log('AfterViewChecked: ' + this._getMessage());
|
||||
}
|
||||
|
||||
private _getMessage(): string {
|
||||
let cmp = this.viewChild;
|
||||
return cmp ? `"${cmp.hero}" child view` : 'no child view';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
|
||||
import {AfterContentParentComponent} from './after-content.component';
|
||||
import {AfterViewParentComponent} from './after-view.component';
|
||||
import {CounterParentComponent} from './counter.component';
|
||||
import {OnChangesParentComponent} from './on-changes.component';
|
||||
import {PeekABooParentComponent} from './peek-a-boo-parent.component';
|
||||
import {SpyParentComponent} from './spy.component';
|
||||
|
||||
/***************************************/
|
||||
/*
|
||||
template: `
|
||||
<peek-a-boo-parent></peek-a-boo-parent>
|
||||
<on-changes-parent></on-changes-parent>
|
||||
<after-view-parent></after-view-parent>
|
||||
<after-content-parent></after-content-parent>
|
||||
<spy-parent></spy-parent>
|
||||
<counter-parent></counter-parent>
|
||||
`,
|
||||
*/
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<peek-a-boo-parent></peek-a-boo-parent>
|
||||
<on-changes-parent></on-changes-parent>
|
||||
<after-view-parent></after-view-parent>
|
||||
<after-content-parent></after-content-parent>
|
||||
<spy-parent></spy-parent>
|
||||
<counter-parent></counter-parent>
|
||||
`,
|
||||
directives: [
|
||||
AfterContentParentComponent,
|
||||
AfterViewParentComponent,
|
||||
OnChangesParentComponent,
|
||||
PeekABooParentComponent,
|
||||
SpyParentComponent,
|
||||
CounterParentComponent
|
||||
]
|
||||
})
|
||||
export class AppComponent {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
import {bootstrap} from 'angular2/platform/browser';
|
||||
import {AppComponent} from './app.component';
|
||||
|
||||
bootstrap(AppComponent).catch(err => console.error(err));
|
|
@ -0,0 +1,20 @@
|
|||
// #docregion
|
||||
import {Component, Input} from 'angular2/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-child',
|
||||
template: `
|
||||
<div class="my-child">
|
||||
<div>-- child view begins --</div>
|
||||
<div class="child">{{hero}} is my hero.</div>
|
||||
<div>-- child view ends --</div>
|
||||
</div>
|
||||
`,
|
||||
styles: [
|
||||
'.child {background: Yellow; padding: 8px; }',
|
||||
'.my-child {background: LightYellow; padding: 8px; margin-top: 8px}'
|
||||
]
|
||||
})
|
||||
export class ChildComponent {
|
||||
@Input() hero: string;
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// #docregion
|
||||
import {
|
||||
Component, Input, Output,
|
||||
OnChanges, SimpleChange,
|
||||
} from 'angular2/core';
|
||||
|
||||
import {Spy} from './spy.directive';
|
||||
import {LoggerService} from './logger.service';
|
||||
|
||||
@Component({
|
||||
selector: 'my-counter',
|
||||
template: `
|
||||
<div class="counter">
|
||||
Counter = {{counter}}
|
||||
|
||||
<h5>-- Counter Change Log --</h5>
|
||||
<div *ngFor="#chg of changeLog" my-spy>{{chg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}'],
|
||||
directives:[Spy]
|
||||
})
|
||||
export class MyCounter implements OnChanges {
|
||||
@Input() counter: number;
|
||||
changeLog:string[] = [];
|
||||
|
||||
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
|
||||
|
||||
// Empty the changeLog whenever counter goes to zero
|
||||
// hint: this is a way to respond programmatically to external value changes.
|
||||
if (this.counter === 0) {
|
||||
this.changeLog.length = 0;
|
||||
}
|
||||
|
||||
// A change to `counter` is the only change we care about
|
||||
let prop = changes['counter'];
|
||||
let cur = prop.currentValue;
|
||||
let prev = JSON.stringify(prop.previousValue); // first time is {}; after is integer
|
||||
this.changeLog.push(`counter: currentValue = ${cur}, previousValue = ${prev}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
|
||||
@Component({
|
||||
selector: 'counter-parent',
|
||||
template: `
|
||||
<div class="parent">
|
||||
<h2>Counter Spy</h2>
|
||||
|
||||
<button (click)="updateCounter()">Update counter</button>
|
||||
<button (click)="reset()">Reset Counter</button>
|
||||
|
||||
<my-counter [counter]="value"></my-counter>
|
||||
|
||||
<h4>-- Spy Lifecycle Hook Log --</h4>
|
||||
<div *ngFor="#msg of spyLog">{{msg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.parent {background: gold; padding: 10px; margin:100px 8px;}'],
|
||||
directives: [MyCounter],
|
||||
providers: [LoggerService]
|
||||
})
|
||||
export class CounterParentComponent {
|
||||
value: number;
|
||||
spyLog:string[] = [];
|
||||
|
||||
private _logger:LoggerService;
|
||||
|
||||
constructor(logger:LoggerService){
|
||||
this._logger = logger;
|
||||
this.spyLog = logger.logs;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
updateCounter() {
|
||||
this.value += 1;
|
||||
}
|
||||
|
||||
reset(){
|
||||
this._logger.log('-- reset --');
|
||||
this.value=0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import {Injectable} from 'angular2/core';
|
||||
|
||||
@Injectable()
|
||||
export class LoggerService {
|
||||
logs:string[] = [];
|
||||
|
||||
log(msg:string, noTick:boolean = false) {
|
||||
if (!noTick) { this.tick(); }
|
||||
this.logs.push(msg);
|
||||
}
|
||||
|
||||
clear() {this.logs.length = 0;}
|
||||
|
||||
tick() {
|
||||
setTimeout(() => {
|
||||
// console.log('tick')
|
||||
}, 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// #docregion
|
||||
import {
|
||||
Component, Input, Output,
|
||||
OnChanges, SimpleChange,
|
||||
} from 'angular2/core';
|
||||
|
||||
|
||||
export class Hero {
|
||||
constructor(public name:string){}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-hero',
|
||||
template: `
|
||||
<div class="hero">
|
||||
<p>{{hero.name}} can {{power}}</p>
|
||||
|
||||
<h4>-- Change Log --</h4>
|
||||
<div *ngFor="#chg of changeLog">{{chg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: [
|
||||
'.hero {background: LightYellow; padding: 8px; margin-top: 8px}',
|
||||
'p {background: Yellow; padding: 8px; margin-top: 8px}'
|
||||
]
|
||||
})
|
||||
export class MyHeroComponent implements OnChanges {
|
||||
@Input() hero: Hero;
|
||||
@Input() power: string;
|
||||
@Input() reset: {};
|
||||
|
||||
changeLog:string[] = [];
|
||||
|
||||
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
|
||||
|
||||
// Empty the changeLog whenever 'reset' property changes
|
||||
// hint: this is a way to respond programmatically to external value changes.
|
||||
if (changes['reset']) { this.changeLog.length = 0; }
|
||||
|
||||
for (let propName in changes) {
|
||||
let prop = changes[propName];
|
||||
let cur = JSON.stringify(prop.currentValue)
|
||||
let prev = JSON.stringify(prop.previousValue); // first time is {}; after is integer
|
||||
this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
|
||||
@Component({
|
||||
selector: 'on-changes-parent',
|
||||
template: `
|
||||
<div class="parent">
|
||||
<h2>OnChanges</h2>
|
||||
|
||||
<div>Hero.name: <input [(ngModel)]="hero.name"> <i>does NOT trigger onChanges</i></div>
|
||||
<div>Power: <input [(ngModel)]="power"> <i>DOES trigger onChanges</i></div>
|
||||
<div><button (click)="reset()">Reset Log</button> <i>triggers onChanges and clears the change log</i></div>
|
||||
|
||||
<my-hero [hero]="hero" [power]="power" [reset]="resetTrigger"></my-hero>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.parent {background: Lavender; padding: 10px; margin:100px 8px;}'],
|
||||
directives: [MyHeroComponent]
|
||||
})
|
||||
export class OnChangesParentComponent {
|
||||
hero:Hero;
|
||||
power:string;
|
||||
resetTrigger = false;
|
||||
|
||||
constructor() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset(){
|
||||
// new Hero object every time; triggers onChange
|
||||
this.hero = new Hero('Windstorm');
|
||||
// setting power only triggers onChange if this value is different
|
||||
this.power = 'sing';
|
||||
// always triggers onChange ... which is interpreted as a reset
|
||||
this.resetTrigger = !this.resetTrigger;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
import {PeekABooComponent} from './peek-a-boo.component'
|
||||
import {LoggerService} from './logger.service';
|
||||
|
||||
@Component({
|
||||
selector: 'peek-a-boo-parent',
|
||||
template: `
|
||||
<div class="parent">
|
||||
<h2>Peek-A-Boo</h2>
|
||||
|
||||
<button (click)="toggleChild()">
|
||||
{{hasChild ? 'Destroy' : 'Create'}} PeekABooComponent
|
||||
</button>
|
||||
<button (click)="updateHero()" [hidden]="!hasChild">Update Hero</button>
|
||||
|
||||
<peek-a-boo *ngIf="hasChild" [name]="heroName">
|
||||
</peek-a-boo>
|
||||
|
||||
<h4>-- Lifecycle Hook Log --</h4>
|
||||
<div *ngFor="#msg of hookLog">{{msg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.parent {background: moccasin; padding: 10px; margin:100px 8px}'],
|
||||
directives: [PeekABooComponent],
|
||||
providers: [LoggerService]
|
||||
})
|
||||
export class PeekABooParentComponent {
|
||||
|
||||
hasChild = false;
|
||||
hookLog:string[];
|
||||
|
||||
heroName = 'Windstorm';
|
||||
private _logger:LoggerService;
|
||||
|
||||
constructor(logger:LoggerService){
|
||||
this._logger = logger;
|
||||
this.hookLog = logger.logs;
|
||||
}
|
||||
|
||||
toggleChild() {
|
||||
this.hasChild = !this.hasChild;
|
||||
if (this.hasChild) {
|
||||
this.heroName = 'Windstorm';
|
||||
this._logger.clear(); // clear log on create
|
||||
}
|
||||
this._logger.tick();
|
||||
}
|
||||
|
||||
updateHero() {
|
||||
this.heroName += '!';
|
||||
this._logger.tick();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
// #docregion
|
||||
// #docregion lc-imports
|
||||
import {
|
||||
OnChanges, SimpleChange,
|
||||
OnInit,
|
||||
// DoCheck, // not demonstrated
|
||||
AfterContentInit,
|
||||
AfterContentChecked,
|
||||
AfterViewInit,
|
||||
AfterViewChecked,
|
||||
OnDestroy
|
||||
} from 'angular2/core';
|
||||
// #docregion lc-imports
|
||||
import {Component, Input, Output} from 'angular2/core';
|
||||
import {LoggerService} from './logger.service';
|
||||
|
||||
let nextId = 1;
|
||||
|
||||
@Component({
|
||||
selector: 'peek-a-boo',
|
||||
template: '<p>Now you see my hero, {{name}}</p>',
|
||||
styles: ['p {background: LightYellow; padding: 8px}']
|
||||
})
|
||||
// Don't HAVE to mention the Lifecycle Hook interfaces
|
||||
// unless we want typing and tool support.
|
||||
export class PeekABooComponent
|
||||
implements OnChanges, OnInit,AfterContentInit,AfterContentChecked,
|
||||
AfterViewInit, AfterViewChecked, OnDestroy {
|
||||
@Input() name:string;
|
||||
|
||||
private _afterContentCheckedCounter = 1;
|
||||
private _afterViewCheckedCounter = 1;
|
||||
private _id = nextId++;
|
||||
private _logger:LoggerService;
|
||||
private _onChangesCounter = 1;
|
||||
private _verb = 'initialized';
|
||||
|
||||
constructor(logger:LoggerService){
|
||||
this._logger = logger;
|
||||
}
|
||||
|
||||
// only called if there is an @input variable set by parent.
|
||||
ngOnChanges(changes: {[propertyName: string]: SimpleChange}){
|
||||
let changesMsgs:string[] = []
|
||||
for (let propName in changes) {
|
||||
if (propName === 'name') {
|
||||
let name = changes['name'].currentValue;
|
||||
changesMsgs.push(`name ${this._verb} to "${name}"`);
|
||||
} else {
|
||||
changesMsgs.push(propName + ' ' + this._verb);
|
||||
}
|
||||
}
|
||||
this._logIt(`onChanges (${this._onChangesCounter++}): ${changesMsgs.join('; ')}`);
|
||||
this._verb = 'changed'; // next time it will be a change
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._logIt(`onInit`);
|
||||
}
|
||||
|
||||
ngAfterContentInit(){
|
||||
this._logIt(`afterContentInit`);
|
||||
}
|
||||
|
||||
// Called after every change detection check
|
||||
// of the component (directive) CONTENT
|
||||
// Beware! Called frequently!
|
||||
ngAfterContentChecked(){
|
||||
let counter = this._afterContentCheckedCounter++;
|
||||
let msg = `afterContentChecked (${counter})`;
|
||||
this._logIt(msg);
|
||||
}
|
||||
|
||||
ngAfterViewInit(){
|
||||
this._logIt(`afterViewInit`);
|
||||
}
|
||||
|
||||
// Called after every change detection check
|
||||
// of the component (directive) VIEW
|
||||
// Beware! Called frequently!
|
||||
|
||||
ngAfterViewChecked(){
|
||||
let counter = this._afterViewCheckedCounter++;
|
||||
let msg = `afterViewChecked (${counter})`;
|
||||
this._logIt(msg);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._logIt(`onDestroy`);
|
||||
}
|
||||
|
||||
private _logIt(msg:string){
|
||||
// Don't tick or else
|
||||
// the AfterContentChecked and AfterViewChecked recurse.
|
||||
// Let parent call tick()
|
||||
this._logger.log(`#${this._id } ${msg}`, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// #docregion
|
||||
import {Component} from 'angular2/core';
|
||||
import {LoggerService} from './logger.service';
|
||||
import {Spy} from './spy.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'spy-parent',
|
||||
template: `
|
||||
<div class="parent">
|
||||
<h2>Spy Directive</h2>
|
||||
|
||||
<input [(ngModel)]="newName" (keyup.enter)="addHero()">
|
||||
<button (click)="addHero()">Add Hero</button>
|
||||
<button (click)="reset()">Reset Heroes</button>
|
||||
|
||||
<p></p>
|
||||
<div *ngFor="#hero of heroes" my-spy class="heroes">
|
||||
{{hero}}
|
||||
</div>
|
||||
|
||||
<h4>-- Spy Lifecycle Hook Log --</h4>
|
||||
<div *ngFor="#msg of spyLog">{{msg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: [
|
||||
'.parent {background: khaki; padding: 10px; margin:100px 8px}',
|
||||
'.heroes {background: LightYellow; padding: 0 8px}'
|
||||
],
|
||||
directives: [Spy],
|
||||
providers: [LoggerService]
|
||||
})
|
||||
export class SpyParentComponent {
|
||||
newName = 'Herbie';
|
||||
heroes:string[] = ['Windstorm', 'Magneta'];
|
||||
spyLog:string[];
|
||||
|
||||
private _logger:LoggerService;
|
||||
|
||||
constructor(logger:LoggerService){
|
||||
this._logger = logger;
|
||||
this.spyLog = logger.logs;
|
||||
}
|
||||
|
||||
addHero() {
|
||||
if (this.newName.trim()) {
|
||||
this.heroes.push(this.newName.trim());
|
||||
this.newName = '';
|
||||
}
|
||||
}
|
||||
|
||||
reset(){
|
||||
this._logger.log('-- reset --');
|
||||
this.heroes.length = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// #docregion
|
||||
import {Directive, Input,
|
||||
OnInit, OnDestroy} from 'angular2/core';
|
||||
|
||||
import {LoggerService} from './logger.service';
|
||||
|
||||
/***************************************/
|
||||
let nextId = 1;
|
||||
|
||||
// Spy on any element to which it is applied.
|
||||
// Usage: <div my-spy>...</div>
|
||||
@Directive({selector: '[my-spy]'})
|
||||
export class Spy implements OnInit, OnDestroy {
|
||||
|
||||
private _id = nextId++;
|
||||
private _logger:LoggerService;
|
||||
|
||||
constructor(logger:LoggerService){
|
||||
this._logger = logger;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._logIt(`onInit`);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._logIt(`onDestroy`);
|
||||
}
|
||||
|
||||
private _logIt(msg:string){
|
||||
this._logger.log(`Spy #${this._id } ${msg}`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- #docregion -->
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Angular 2 Lifecycle Hooks</title>
|
||||
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
<script src="node_modules/rxjs/bundles/Rx.js"></script>
|
||||
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
|
||||
<script>
|
||||
System.config({
|
||||
packages: {
|
||||
app: {
|
||||
format: 'register',
|
||||
defaultExtension: 'js'
|
||||
}
|
||||
}
|
||||
});
|
||||
System.import('app/boot')
|
||||
.then(null, console.error.bind(console));
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"description": "Lifecycle Hooks",
|
||||
"files":["!**/*.d.ts", "!**/*.js"],
|
||||
"tags": ["lifecycle", "hooks",
|
||||
"onInit", "onDestroy", "onChange",
|
||||
"ngOnInit", "ngOnDestroy", "ngOnChange"]
|
||||
}
|
|
@ -48,6 +48,11 @@
|
|||
"intro": "Discover the basics of screen navigation with the Angular 2 router"
|
||||
},
|
||||
|
||||
"lifecycle-hooks": {
|
||||
"title": "Lifecycle Hooks",
|
||||
"intro": "Angular calls lifecycle hook methods on our directives and components as it creates, changes, and destroys them."
|
||||
},
|
||||
|
||||
"attribute-directives": {
|
||||
"title": "Attribute Directives",
|
||||
"intro": "Attribute directives attach behavior to elements."
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
include ../../../../_includes/_util-fns
|
||||
|
||||
:marked
|
||||
# Component Lifecycle
|
||||
A Component has a lifecycle managed by Angular itself. Angular creates it, renders it, creates and renders its children,
|
||||
checks it when its data-bound properties change, and destroys it before removing it from the DOM.
|
||||
|
||||
Angular offers **Lifecycle hooks**
|
||||
that give us visibility into these key moments and the ability to act when they occur.
|
||||
|
||||
We cover these hooks in this chapter and demonstrate how they work in code.
|
||||
|
||||
[Live Example](/resources/live-examples/lifecycle-hooks/ts/plnkr.html)
|
||||
|
||||
<!--
|
||||
https://github.com/angular/angular/blob/master/modules/angular2/src/core/linker/interfaces.ts
|
||||
-->
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## The Lifecycle Hooks
|
||||
Directive and component instances have a lifecycle
|
||||
as Angular creates, updates, and destroys them.
|
||||
|
||||
Developers can tap into key moments in that lifecycle by implementing
|
||||
one or more of the "Lifecycle Hook" interfaces, all of them available
|
||||
in the `angular2/core` library.
|
||||
|
||||
Here is the complete lifecycle hook interface inventory:
|
||||
|
||||
* `OnInit`
|
||||
* `OnDestroy`
|
||||
* `DoCheck`
|
||||
* `OnChanges`
|
||||
* `AfterContentInit`
|
||||
* `AfterContentChecked`
|
||||
* `AfterViewInit`
|
||||
* `AfterViewChecked`
|
||||
|
||||
No directive or component will implement all of them and some of them only make
|
||||
sense for components.
|
||||
|
||||
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
||||
For example, the `OnInit` interface has a hook method names `ngOnInit`.
|
||||
|
||||
Angular calls these hook methods in the following order:
|
||||
* `ngOnChanges` - called when an input or output binding value changes
|
||||
* `ngOnInit` - after the first `ngOnChanges`
|
||||
* `ngDoCheck` - developer's custom change detection
|
||||
* `ngAfterContentInit` - after component content initialized
|
||||
* `ngAfterContentChecked` - after every check of component content
|
||||
* `ngAfterViewInit` - after component's view(s) are initialized
|
||||
* `ngAfterViewChecked` - after every check of a component's view(s)
|
||||
* `ngOnDestroy` - just before the directive is destroyed.
|
||||
|
||||
The [live example](/resources/live-examples/lifecycle-hooks/ts/plnkr.html) demonstrates
|
||||
these hooks.
|
||||
|
||||
:marked
|
||||
## Peek-a-boo
|
||||
The `PeekABooComponent` demonstrates all of the hooks in the same component.
|
||||
.l-sub-section
|
||||
:marked
|
||||
Except for `DoCheck`. If our component superseded regular Angular change detection
|
||||
with its own change detection processing
|
||||
we would also add a `ngDoCheck` method. We would **not** implement `ngOnChanges`.
|
||||
We write either `ngOnChanges` or `ngDoCheck`, not both.
|
||||
|
||||
Custom change detection and `ngDoCheck` are on our documentation backlog.
|
||||
:marked
|
||||
Peek-a-boo is a demo. We'd rarely if ever implement all interfaces like this in real life.
|
||||
|
||||
We look forward to explaining the Peek-a-boo example and the other lifecycle hook examples in
|
||||
an update to this chapter. Meanwhile, please enjoy poking around in the
|
||||
[code](/resources/live-examples/lifecycle-hooks/ts/plnkr.html).
|
||||
|
||||
## Interface optional?
|
||||
The lifecycle interfaces are optional.
|
||||
We recommend adding them to benefit from TypeScript's strong typing and editor tooling.
|
||||
|
||||
But they disappear from the transpiled JavaScript.
|
||||
Angular can't see them at runtime. And they are useless to someone developing in
|
||||
a language without interfaces (such as pure JavaScript).
|
||||
|
||||
Fortunately, they aren't necessary.
|
||||
We don't have to add the lifecycle hook interfaces to our directives and components to benefit from the hooks themselves.
|
||||
|
||||
Angular instead inspects our directive and component classes
|
||||
and calls the hook methods *if they are defined*.
|
||||
Angular will find and call methods like `ngOnInit()`, with or without the interfaces.
|
Loading…
Reference in New Issue