import { AfterContentChecked, AfterContentInit, Component, ContentChild } from '@angular/core';
import { LoggerService } from './logger.service';
//////////////////
@Component({
selector: 'my-child',
template: '<input [(ngModel)]="hero">'
})
export class ChildComponent {
hero = 'Magneta';
}
//////////////////////
@Component({
selector: 'after-content',
template: `
<div>-- projected content begins --</div>
<ng-content></ng-content>
<div>-- projected content ends --</div>`
+ `
<p *ngIf="comment" class="comment">
{{comment}}
</p>
`
})
export class AfterContentComponent implements AfterContentChecked, AfterContentInit {
private prevHero = '';
comment = '';
// Query for a CONTENT child of type `ChildComponent`
@ContentChild(ChildComponent) contentChild: ChildComponent;
constructor(private logger: LoggerService) {
this.logIt('AfterContent constructor');
}
ngAfterContentInit() {
// contentChild is set after the content has been initialized
this.logIt('AfterContentInit');
this.doSomething();
}
ngAfterContentChecked() {
// contentChild is updated after the content has been checked
if (this.prevHero === this.contentChild.hero) {
this.logIt('AfterContentChecked (no change)');
} else {
this.prevHero = this.contentChild.hero;
this.logIt('AfterContentChecked');
this.doSomething();
}
}
// This surrogate for real business logic sets the `comment`
private doSomething() {
this.comment = this.contentChild.hero.length > 10 ? `That's a long name` : '';
}
private logIt(method: string) {
let child = this.contentChild;
let message = `${method}: ${child ? child.hero : 'no'} child content`;
this.logger.log(message);
}
// ...
}
//////////////
@Component({
selector: 'after-content-parent',
template: `
<div class="parent">
<h2>AfterContent</h2>
<div *ngIf="show">` +
`<after-content>
<my-child></my-child>
</after-content>`
+ `</div>
<h4>-- AfterContent Logs --</h4>
<p><button (click)="reset()">Reset</button></p>
<div *ngFor="let msg of logs">{{msg}}</div>
</div>
`,
styles: ['.parent {background: burlywood}'],
providers: [LoggerService],
directives: [AfterContentComponent, ChildComponent]
})
export class AfterContentParentComponent {
logs: string[];
show = true;
constructor(private logger: LoggerService) {
this.logs = logger.logs;
}
reset() {
this.logs.length = 0;
// quickly remove and reload AfterContentComponent which recreates it
this.show = false;
this.logger.tick_then(() => this.show = true);
}
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { AfterViewChecked, AfterViewInit, Component, ViewChild } from '@angular/core';
import { LoggerService } from './logger.service';
//////////////////
@Component({
selector: 'my-child',
template: '<input [(ngModel)]="hero">'
})
export class ChildViewComponent {
hero = 'Magneta';
}
//////////////////////
@Component({
selector: 'after-view',
template: `
<div>-- child view begins --</div>
<my-child></my-child>
<div>-- child view ends --</div>`
+ `
<p *ngIf="comment" class="comment">
{{comment}}
</p>
`,
directives: [ChildViewComponent]
})
export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
private prevHero = '';
// Query for a VIEW child of type `ChildViewComponent`
@ViewChild(ChildViewComponent) viewChild: ChildViewComponent;
constructor(private logger: LoggerService) {
this.logIt('AfterView constructor');
}
ngAfterViewInit() {
// viewChild is set after the view has been initialized
this.logIt('AfterViewInit');
this.doSomething();
}
ngAfterViewChecked() {
// viewChild is updated after the view has been checked
if (this.prevHero === this.viewChild.hero) {
this.logIt('AfterViewChecked (no change)');
} else {
this.prevHero = this.viewChild.hero;
this.logIt('AfterViewChecked');
this.doSomething();
}
}
comment = '';
// This surrogate for real business logic sets the `comment`
private doSomething() {
let c = this.viewChild.hero.length > 10 ? `That's a long name` : '';
if (c !== this.comment) {
// Wait a tick because the component's view has already been checked
this.logger.tick_then(() => this.comment = c);
}
}
private logIt(method: string) {
let child = this.viewChild;
let message = `${method}: ${child ? child.hero : 'no'} child view`;
this.logger.log(message);
}
// ...
}
//////////////
@Component({
selector: 'after-view-parent',
template: `
<div class="parent">
<h2>AfterView</h2>
<after-view *ngIf="show"></after-view>
<h4>-- AfterView Logs --</h4>
<p><button (click)="reset()">Reset</button></p>
<div *ngFor="let msg of logs">{{msg}}</div>
</div>
`,
styles: ['.parent {background: burlywood}'],
providers: [LoggerService],
directives: [AfterViewComponent]
})
export class AfterViewParentComponent {
logs: string[];
show = true;
constructor(private logger: LoggerService) {
this.logs = logger.logs;
}
reset() {
this.logs.length = 0;
// quickly remove and reload AfterViewComponent which recreates it
this.show = false;
this.logger.tick_then(() => this.show = true);
}
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Component } from '@angular/core';
import { AfterContentParentComponent } from './after-content.component';
import { AfterViewParentComponent } from './after-view.component';
import { CounterParentComponent } from './counter.component';
import { DoCheckParentComponent } from './do-check.component';
import { OnChangesParentComponent } from './on-changes.component';
import { PeekABooParentComponent } from './peek-a-boo-parent.component';
import { SpyParentComponent } from './spy.component';
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [
AfterContentParentComponent,
AfterViewParentComponent,
CounterParentComponent,
DoCheckParentComponent,
OnChangesParentComponent,
PeekABooParentComponent,
SpyParentComponent,
]
})
export class AppComponent {
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import {
Component, Input,
OnChanges, SimpleChange,
} from '@angular/core';
import { SpyDirective } 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="let chg of changeLog" mySpy>{{chg}}</div>
</div>
`,
styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}'],
directives: [SpyDirective]
})
export class MyCounterComponent 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 chng = changes['counter'];
let cur = chng.currentValue;
let prev = JSON.stringify(chng.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="let msg of spyLog">{{msg}}</div>
</div>
`,
styles: ['.parent {background: gold;}'],
directives: [MyCounterComponent],
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;
this.logger.tick();
}
reset() {
this.logger.log('-- reset --');
this.value = 0;
this.logger.tick();
}
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
/* tslint:disable:forin */
import { Component, DoCheck, Input, OnChanges, SimpleChange, ViewChild } from '@angular/core';
class Hero {
constructor(public name: string) {}
}
@Component({
selector: 'do-check',
template: `
<div class="hero">
<p>{{hero.name}} can {{power}}</p>
<h4>-- Change Log --</h4>
<div *ngFor="let 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 DoCheckComponent implements DoCheck, OnChanges {
@Input() hero: Hero;
@Input() power: string;
changeDetected = false;
changeLog: string[] = [];
oldHeroName = '';
oldPower = '';
oldLogLength = 0;
noChangeCount = 0;
ngDoCheck() {
if (this.hero.name !== this.oldHeroName) {
this.changeDetected = true;
this.changeLog.push(`DoCheck: Hero name changed to "${this.hero.name}" from "${this.oldHeroName}"`);
this.oldHeroName = this.hero.name;
}
if (this.power !== this.oldPower) {
this.changeDetected = true;
this.changeLog.push(`DoCheck: Power changed to "${this.power}" from "${this.oldPower}"`);
this.oldPower = this.power;
}
if (this.changeDetected) {
this.noChangeCount = 0;
} else {
// log that hook was called when there was no relevant change.
let count = this.noChangeCount += 1;
let noChangeMsg = `DoCheck called ${count}x when no change to hero or power`;
if (count === 1) {
// add new "no change" message
this.changeLog.push(noChangeMsg);
} else {
// update last "no change" message
this.changeLog[this.changeLog.length - 1] = noChangeMsg;
}
}
this.changeDetected = false;
}
// Copied from OnChangesComponent
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
this.changeLog.push(`OnChanges: ${propName}: currentValue = ${cur}, previousValue = ${prev}`);
}
}
reset() {
this.changeDetected = true;
this.changeLog.length = 0;
}
}
/***************************************/
@Component({
selector: 'do-check-parent',
templateUrl: 'app/on-changes-parent.component.html',
styles: ['.parent {background: Lavender}'],
directives: [DoCheckComponent]
})
export class DoCheckParentComponent {
hero: Hero;
power: string;
title = 'DoCheck';
@ViewChild(DoCheckComponent) childView: DoCheckComponent;
constructor() { this.reset(); }
reset() {
this.hero = new Hero('Windstorm');
this.power = 'sing';
if (this.childView) { this.childView.reset(); }
}
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Injectable } from '@angular/core';
@Injectable()
export class LoggerService {
logs: string[] = [];
prevMsg = '';
prevMsgCount = 1;
log(msg: string) {
if (msg === this.prevMsg) {
// Repeat message; update last log entry with count.
this.logs[this.logs.length - 1] = msg + ` (${this.prevMsgCount += 1}x)`;
} else {
// New message; log it.
this.prevMsg = msg;
this.prevMsgCount = 1;
this.logs.push(msg);
}
}
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); }
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
bootstrap(AppComponent).catch(err => console.error(err));
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
/* tslint:disable:forin */
import {
Component, Input, OnChanges,
SimpleChange, ViewChild
} from '@angular/core';
class Hero {
constructor(public name: string) {}
}
@Component({
selector: 'on-changes',
template: `
<div class="hero">
<p>{{hero.name}} can {{power}}</p>
<h4>-- Change Log --</h4>
<div *ngFor="let 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 OnChangesComponent implements OnChanges {
@Input() hero: Hero;
@Input() power: string;
changeLog: string[] = [];
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
let prev = JSON.stringify(chng.previousValue);
this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
}
}
reset() { this.changeLog.length = 0; }
}
/***************************************/
@Component({
selector: 'on-changes-parent',
templateUrl: 'app/on-changes-parent.component.html',
styles: ['.parent {background: Lavender;}'],
directives: [OnChangesComponent]
})
export class OnChangesParentComponent {
hero: Hero;
power: string;
title = 'OnChanges';
@ViewChild(OnChangesComponent) childView: OnChangesComponent;
constructor() {
this.reset();
}
reset() {
// new Hero object every time; triggers onChanges
this.hero = new Hero('Windstorm');
// setting power only triggers onChanges if this value is different
this.power = 'sing';
if (this.childView) { this.childView.reset(); }
}
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Component } from '@angular/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="let msg of hookLog">{{msg}}</div>
</div>
`,
styles: ['.parent {background: moccasin}'],
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();
}
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import {
AfterContentChecked,
AfterContentInit,
AfterViewChecked,
AfterViewInit,
DoCheck,
OnChanges,
OnDestroy,
OnInit,
SimpleChange
} from '@angular/core';
import { Component, Input } from '@angular/core';
import { LoggerService } from './logger.service';
let nextId = 1;
export class PeekABoo implements OnInit {
constructor(private logger: LoggerService) { }
// implement OnInit's `ngOnInit` method
ngOnInit() { this.logIt(`OnInit`); }
protected logIt(msg: string) {
this.logger.log(`#${nextId++} ${msg}`);
}
}
@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 extends PeekABoo implements
OnChanges, OnInit, DoCheck,
AfterContentInit, AfterContentChecked,
AfterViewInit, AfterViewChecked,
OnDestroy {
@Input() name: string;
private verb = 'initialized';
constructor(logger: LoggerService) {
super(logger);
let is = this.name ? 'is' : 'is not';
this.logIt(`name ${is} known at construction`);
}
// only called for/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: ${changesMsgs.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`); }
// Beware! Called frequently!
// Called in every change detection cycle anywhere on the page
ngAfterViewChecked() { this.logIt(`AfterViewChecked`); }
ngOnDestroy() { this.logIt(`OnDestroy`); }
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Component } from '@angular/core';
import { LoggerService } from './logger.service';
import { SpyDirective } from './spy.directive';
@Component({
selector: 'spy-parent',
templateUrl: 'app/spy.component.html',
styles: [
'.parent {background: khaki;}',
'.heroes {background: LightYellow; padding: 0 8px}'
],
directives: [SpyDirective],
providers: [LoggerService]
})
export class SpyParentComponent {
newName = 'Herbie';
heroes: string[] = ['Windstorm', 'Magneta'];
spyLog: string[];
constructor(private logger: LoggerService) {
this.spyLog = logger.logs;
}
addHero() {
if (this.newName.trim()) {
this.heroes.push(this.newName.trim());
this.newName = '';
this.logger.tick();
}
}
removeHero(hero: string) {
this.heroes.splice(this.heroes.indexOf(hero), 1);
this.logger.tick();
}
reset() {
this.logger.log('-- reset --');
this.heroes.length = 0;
this.logger.tick();
}
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
import { Directive, OnInit, OnDestroy } from '@angular/core';
import { LoggerService } from './logger.service';
let nextId = 1;
// Spy on any element to which it is applied.
// Usage: <div mySpy>...</div>
@Directive({selector: '[mySpy]'})
export class SpyDirective implements OnInit, OnDestroy {
constructor(private logger: LoggerService) { }
ngOnInit() { this.logIt(`onInit`); }
ngOnDestroy() { this.logIt(`onDestroy`); }
private logIt(msg: string) {
this.logger.log(`Spy #${nextId++} ${msg}`);
}
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
.parent {
color: #666;
margin: 14px 0;
padding: 8px;
}
input {
margin: 4px;
padding: 4px;
}
.comment {
color: red;
font-style: italic;
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
/* Master Styles */
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
a {
cursor: pointer;
cursor: hand;
}
button {
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
/* Navigation link styles */
nav a {
padding: 5px 10px;
text-decoration: none;
margin-top: 10px;
display: inline-block;
background-color: #eee;
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
}
nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.active {
color: #039be5;
}
/* items class */
.items {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 24em;
}
.items li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.items li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.items li.selected:hover {
background-color: #BBD8DC;
color: white;
}
.items .text {
position: relative;
top: -3px;
}
.items {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 24em;
}
.items li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.items li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.items li.selected {
background-color: #CFD8DC;
color: white;
}
.items li.selected:hover {
background-color: #BBD8DC;
}
.items .text {
position: relative;
top: -3px;
}
.items .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
<a id="top"></a>
<h1>Component Lifecycle Hooks</h1>
<a href="#hooks">Peek-a-boo: (most) lifecycle hooks</a><br>
<a href="#onchanges">OnChanges</a><br>
<a href="#docheck">DoCheck</a><br>
<a href="#after-view">AfterViewInit & AfterViewChecked</a><br>
<a href="#after-content">AfterContentInit & AfterContentChecked</a><br>
<a href="#spy">Spy: directive with OnInit & OnDestroy</a><br>
<a href="#counter">Counter: OnChanges + Spy directive</a><br>
<a id="hooks"></a>
<peek-a-boo-parent></peek-a-boo-parent>
<a href="#top">back to top</a>
<a id="spy"></a>
<spy-parent></spy-parent>
<a href="#top">back to top</a>
<a id="onchanges"></a>
<on-changes-parent></on-changes-parent>
<a href="#top">back to top</a>
<a id="docheck"></a>
<do-check-parent></do-check-parent>
<a href="#top">back to top</a>
<a id="after-view"></a>
<after-view-parent></after-view-parent>
<a href="#top">back to top</a>
<a id="after-content"></a>
<after-content-parent></after-content-parent>
<a href="#top">back to top</a>
<a id="counter"></a>
<counter-parent></counter-parent>
<a href="#top">back to top</a>
<!--
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->
<div class="parent">
<h2>{{title}}</h2>
<table>
<tr><td>Power: </td><td><input [(ngModel)]="power"></td></tr>
<tr><td>Hero.name: </td><td><input [(ngModel)]="hero.name"></td></tr>
</table>
<p><button (click)="reset()">Reset Log</button></p>
<on-changes [hero]="hero" [power]="power"></on-changes>
<do-check [hero]="hero" [power]="power"></do-check>
</div>
<!--
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->
<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="let hero of heroes" mySpy class="heroes">
{{hero}}
</div>
<h4>-- Spy Lifecycle Hook Log --</h4>
<div *ngFor="let msg of spyLog">{{msg}}</div>
</div>
<!--
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->
<!DOCTYPE html>
<html>
<head>
<title>Angular 2 Lifecycle Hooks</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="sample.css">
<!-- Polyfill(s) for older browsers -->
<script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
<script src="https://npmcdn.com/zone.js@0.6.12?main=browser"></script>
<script src="https://npmcdn.com/reflect-metadata@0.1.3"></script>
<script src="https://npmcdn.com/systemjs@0.19.27/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>
<!--
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->
/**
* PLUNKER VERSION (based on systemjs.config.js in angular.io)
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function(global) {
var ngVer = '@2.0.0-rc.4'; // lock in the angular package version; do not let it float to current!
var routerVer = '@3.0.0-beta.1'; // lock router version
var formsVer = '@0.2.0'; // lock forms version
var routerDeprecatedVer = '@2.0.0-rc.2'; // temporarily until we update all the guides
//map tells the System loader where to look for things
var map = {
'app': 'app',
'@angular': 'https://npmcdn.com/@angular', // sufficient if we didn't pin the version
'@angular/router': 'https://npmcdn.com/@angular/router' + routerVer,
'@angular/forms': 'https://npmcdn.com/@angular/forms' + formsVer,
'@angular/router-deprecated': 'https://npmcdn.com/@angular/router-deprecated' + routerDeprecatedVer,
'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api', // get latest
'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6',
'ts': 'https://npmcdn.com/plugin-typescript@4.0.10/lib/plugin.js',
'typescript': 'https://npmcdn.com/typescript@1.9.0-dev.20160409/lib/typescript.js',
};
//packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.ts', defaultExtension: 'ts' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
};
var ngPackageNames = [
'common',
'compiler',
'core',
'http',
'platform-browser',
'platform-browser-dynamic',
'upgrade',
];
// Add map entries for each angular package
// only because we're pinning the version with `ngVer`.
ngPackageNames.forEach(function(pkgName) {
map['@angular/'+pkgName] = 'https://npmcdn.com/@angular/' + pkgName + ngVer;
});
// Add package entries for angular packages
ngPackageNames.forEach(function(pkgName) {
// Bundled (~40 requests):
packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
// Individual files (~300 requests):
//packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
// No umd for router yet
packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' };
// Forms not on rc yet
packages['@angular/forms'] = { main: 'index.js', defaultExtension: 'js' };
// Temporarily until we update the guides
packages['@angular/router-deprecated'] = { main: '/bundles/router-deprecated' + '.umd.js', defaultExtension: 'js' };
var config = {
// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
transpiler: 'ts',
typescriptOptions: {
tsconfig: true
},
meta: {
'typescript': {
"exports": "ts"
}
},
map: map,
packages: packages
};
System.config(config);
})(this);
/*
Copyright 2016 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}