parent
82383bc673
commit
1a98ef5844
1
public/docs/_examples/lifecycle-hooks/ts/.gitignore
vendored
Normal file
1
public/docs/_examples/lifecycle-hooks/ts/.gitignore
vendored
Normal file
@ -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 {
|
||||||
|
}
|
4
public/docs/_examples/lifecycle-hooks/ts/app/boot.ts
Normal file
4
public/docs/_examples/lifecycle-hooks/ts/app/boot.ts
Normal file
@ -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}`);
|
||||||
|
}
|
||||||
|
}
|
29
public/docs/_examples/lifecycle-hooks/ts/index.html
Normal file
29
public/docs/_examples/lifecycle-hooks/ts/index.html
Normal file
@ -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>
|
7
public/docs/_examples/lifecycle-hooks/ts/plnkr.json
Normal file
7
public/docs/_examples/lifecycle-hooks/ts/plnkr.json
Normal file
@ -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"
|
"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": {
|
"attribute-directives": {
|
||||||
"title": "Attribute Directives",
|
"title": "Attribute Directives",
|
||||||
"intro": "Attribute directives attach behavior to elements."
|
"intro": "Attribute directives attach behavior to elements."
|
||||||
|
90
public/docs/ts/latest/guide/lifecycle-hooks.jade
Normal file
90
public/docs/ts/latest/guide/lifecycle-hooks.jade
Normal file
@ -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…
x
Reference in New Issue
Block a user