docs(template-syntax): add TOC, NgSwitch, and NgForTrackBy - TS/Dart

This commit is contained in:
Ward Bell
2016-02-06 17:18:26 -08:00
parent d4914eed26
commit ee5a4e131a
11 changed files with 670 additions and 160 deletions
@@ -28,9 +28,6 @@ class AppComponent {
// String badCurly = 'bad curly'; // XXX: This isn't working.
String badCurly = 'bad'; // XXX: This isn't working.
// List<String> badCurly = ['bad', 'curly']; // XXX: This isn't working.
String title = 'Template Syntax';
String toeChoice;
int val = 2;
bool canSave = true;
bool isActive = false;
bool isSpecial = true;
@@ -143,6 +140,8 @@ class AppComponent {
}
// #enddocregion setStyles
String title = 'Template Syntax';
String toeChoice;
String toeChooser(Element picker) {
List<Element> choices = picker.children;
for (var i = 0; i < choices.length; i++) {
@@ -153,4 +152,51 @@ class AppComponent {
}
}
}
// #docregion trackByHeroes
int trackByHeroes(int index, Hero hero) { return hero.id; }
// #enddocregion trackByHeroes
// #docregion trackById
int trackById(int index, Map item): string { return item['id']; }
// #enddocregion trackById
int val = 2;
//////// Detect effects of NgForTrackBy ///////////////
int heroesNoTrackByChangeCount = 0;
int heroesWithTrackByChangeCount = 0;
/*
// Convert to Dart
@ViewChildren('noTrackBy') childrenNoTrackBy:QueryList<ElementRef>;
@ViewChildren('withTrackBy') childrenWithTrackBy:QueryList<ElementRef>;
private _oldNoTrackBy:HTMLElement[];
private _oldWithTrackBy:HTMLElement[];
private _detectNgForTrackByEffects() {
this._oldNoTrackBy = toArray(this.childrenNoTrackBy);
this._oldWithTrackBy = toArray(this.childrenWithTrackBy);
this.childrenNoTrackBy.changes.subscribe((changes:any) => {
let newNoTrackBy = toArray(changes);
let isSame = this._oldNoTrackBy.every((v:any, i:number) => v === newNoTrackBy[i]);
if (!isSame) {
this._oldNoTrackBy = newNoTrackBy;
this.heroesNoTrackByChangeCount++;
}
})
this.childrenWithTrackBy.changes.subscribe((changes:any) => {
let newWithTrackBy = toArray(changes);
let isSame = this._oldWithTrackBy.every((v:any, i:number) => v === newWithTrackBy[i]);
if (!isSame) {
this._oldWithTrackBy = newWithTrackBy;
this.heroesWithTrackByChangeCount++;
}
})
}
*/
///////////////////
}
@@ -1,9 +1,44 @@
<!-- #docregion my-first-app -->
<h3>My First Angular Application</h3>
<!-- #enddocregion my-first-app -->
<!-- #docplaster -->
<a id="toc"></a>
<h1>Template Syntax</h1>
<a href="#interpolation">Interpolation</a><br>
<a href="#mental-model">Mental Model</a><br>
<a href="#buttons">Buttons</a><br>
<a href="#prop-vs-attrib">Properties vs. Attributes</a><br>
<br>
<a href="#property-binding">Property Binding</a><br>
<div style="margin-left:8px">
<a href="#attribute-binding">Attribute Binding</a><br>
<a href="#class-binding">Class Binding</a><br>
<a href="#style-binding">Style Binding</a><br>
</div>
<br>
<a href="#event-binding">Event Binding</a><br>
<br>
<div>Directives</div>
<div style="margin-left:8px">
<a href="#ngModel">NgModel (two-way) Binding</a><br>
<a href="#ngClass">NgClass Binding</a><br>
<a href="#ngStyle">NgStyle Binding</a><br>
<a href="#ngIf">NgIf</a><br>
<a href="#ngSwitch">NgSwitch</a><br>
<a href="#ngFor">NgFor</a><br>
<div style="margin-left:8px">
<a href="#ngFor-index">NgFor with index</a><br>
<a href="#ngFor-trackBy">NgFor with trackBy</a><br>
</div>
</div>
<br>
<a href="#star-prefix">* prefix and &lt;template&gt;</a><br>
<a href="#local-vars">Template local variables</a><br>
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
<a href="#pipes">Pipes</a><br>
<a href="#elvis">Elvis <i>?.</i></a><br>
<!--<a href="#enums">Enums</a><br>-->
<!-- Interpolation and expressions -->
<hr><h2>Interpolation</h2>
<hr><h2 id="interpolation">Interpolation</h2>
<!-- #docregion first-interpolation -->
<p>My current hero is {{currentHero.firstName}}</p>
@@ -26,9 +61,10 @@
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
<!-- #enddocregion sum-2 -->
<a class="to-toc" href="#toc">top</a>
<!-- New Mental Model -->
<hr><h2>New Mental Model</h2>
<hr><h2 id="mental-model">New Mental Model</h2>
<!--<img src="http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png">-->
<!-- Public Domain terms of use: http://www.wpclipart.com/terms.html -->
@@ -65,11 +101,10 @@
</div>
<br><br>
<!-- See https://github.com/angular/angular/issues/5707 about "myClick (myClick)" -->
<!-- #docregion event-binding-syntax-1 -->
<button (click) = "onSave()">Save</button>
<hero-detail (deleted)="onHeroDeleted()"></hero-detail>
<div myClick (myClick)="clickity=$event">click me</div>
<div (myClick)="clickity=$event">click me</div>
<!-- #enddocregion event-binding-syntax-1 -->
{{clickity}}
<br><br>
@@ -97,8 +132,10 @@
<!-- #enddocregion style-binding-syntax-1 -->
button</button>
<a class="to-toc" href="#toc">top</a>
<!-- property vs. attribute -->
<hr><h2>Property vs. Attribute (img examples)</h2>
<hr><h2 id="prop-vs-attrib">Property vs. Attribute (img examples)</h2>
<!-- examine the following <img> tag in the browser tools -->
<img src="assets/images/ng-logo.png"
[src]="heroImageUrl">
@@ -109,10 +146,10 @@ button</button>
<img bind-src="heroImageUrl"/>
<img [attr.src]="villainImageUrl"/>
<a class="to-toc" href="#toc">top</a>
<!-- buttons -->
<hr><h2>Buttons</h2>
<hr><h2 id="buttons">Buttons</h2>
<button>Enabled (but does nothing)</button>
<button disabled>Disabled</button>
@@ -124,9 +161,10 @@ button</button>
<button bind-disabled="isUnchanged" on-click="onSave($event)">Disabled Cancel</button>
<button [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button>
<a class="to-toc" href="#toc">top</a>
<!-- property binding -->
<hr><h2>Property Binding</h2>
<hr><h2 id="property-binding">Property Binding</h2>
<!-- #docregion property-binding-1 -->
<img [src]="heroImageUrl">
@@ -159,8 +197,10 @@ button</button>
<div [textContent]="'The title is ' + title"></div>
<!-- #enddocregion property-binding-vs-interpolation -->
<a class="to-toc" href="#toc">top</a>
<!-- attribute binding -->
<hr><h2>Attribute Binding</h2>
<hr><h2 id="attribute-binding">Attribute Binding</h2>
<!-- create and set a colspan attribute -->
<!-- #docregion attrib-binding-colspan -->
@@ -197,12 +237,10 @@ button</button>
<button disabled [disabled]="false">Enabled (but inert)</button>
</div>
<a class="to-toc" href="#toc">top</a>
<!-- class binding -->
<hr><h2>Class Binding</h2>
<hr><h2 id="class-binding">Class Binding</h2>
<!-- #docregion class-binding-1 -->
<!-- standard class attribute setting -->
@@ -229,10 +267,10 @@ button</button>
<div bind-class.special="isSpecial">This class binding is special too</div>
<a class="to-toc" href="#toc">top</a>
<!--style binding -->
<hr><h2>Style Binding</h2>
<hr><h2 id="style-binding">Style Binding</h2>
<!-- #docregion style-binding-1 -->
<button [style.color] = "isSpecial ? 'red': 'green'">Red</button>
@@ -244,8 +282,10 @@ button</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
<!-- #enddocregion style-binding-2 -->
<a class="to-toc" href="#toc">top</a>
<!-- event binding -->
<hr><h2>Event Binding</h2>
<hr><h2 id="event-binding">Event Binding</h2>
<!-- #docregion event-binding-1 -->
<button (click)="onSave()">Save</button>
@@ -260,7 +300,7 @@ button</button>
<!-- `myClick` is an event on the custom `MyClickDirective` -->
<!-- #docregion my-click -->
<div myClick (myClick)="clickMessage=$event">click with myClick</div>
<div (myClick)="clickMessage=$event">click with myClick</div>
<!-- #enddocregion my-click -->
<!-- #enddocregion event-binding-3 -->
{{clickMessage}}
@@ -301,10 +341,11 @@ button</button>
<!-- #enddocregion event-binding-propagation -->
<br><br>
<a class="to-toc" href="#toc">top</a>
<!-- Two way data binding unwound;
passing the changed display value to the event handler via `$event` -->
<hr><h2>NgModel Binding</h2>
<hr><h2 id="ngModel">NgModel (two-way) Binding</h2>
<h3>Result: {{currentHero.firstName}}</h3>
@@ -339,10 +380,10 @@ bindon-ngModel
(ngModelChange) = "setUpperCaseFirstName($event)"
<br>
<a class="to-toc" href="#toc">top</a>
<!-- NgClass binding -->
<hr><h2>NgClass Binding</h2>
<hr><h2 id="ngClass">NgClass Binding</h2>
<p>setClasses returns {{setClasses() | json}}</p>
<!-- #docregion NgClass-1 -->
@@ -359,6 +400,7 @@ bindon-ngModel
<div class="bad curly special">Bad curly special</div>
<div [ngClass]="{bad:false, curly:true, special:true}">Curly special</div>
<a class="to-toc" href="#toc">top</a>
<!-- NgStyle binding -->
<hr><h2>NgStyle Binding</h2>
@@ -384,10 +426,10 @@ bindon-ngModel
<!-- not used in chapter -->
<a class="to-toc" href="#toc">top</a>
<!-- NgIf binding -->
<hr><h2>NgIf Binding</h2>
<hr><h2 id="ngIf">NgIf Binding</h2>
<!-- #docregion NgIf-1 -->
<div *ngIf="currentHero != null">Hello, {{currentHero.firstName}}</div>
@@ -402,7 +444,6 @@ bindon-ngModel
<hero-detail *ngIf="isActive"></hero-detail>
<!-- #enddocregion NgIf-2 -->
<!-- NgIf binding with template (no *) -->
<template [ngIf]="currentHero != null">Add {{currentHero.firstName}} with template</template>
@@ -425,10 +466,10 @@ bindon-ngModel
<div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
<!-- #enddocregion NgIf-3 -->
<a class="to-toc" href="#toc">top</a>
<!-- NgSwitch binding -->
<hr><h2>NgSwitch Binding</h2>
<hr><h2 id="ngSwitch">NgSwitch Binding</h2>
<fieldset #toePicker (click)="toeChooser(toePicker)" >
<input type="radio" name="toes" value="Eenie">Eenie
@@ -439,23 +480,39 @@ bindon-ngModel
</fieldset>
<div class="toe">
<div *ngIf="toeChoice == null">Pick a toe</div>
<div *ngIf="toeChoice != null">You picked
<!-- #docregion NgSwitch -->
<span [ngSwitch]="toeChoice">
<template [ngSwitchWhen]="'Eenie'">Eenie</template>
<template [ngSwitchWhen]="'Meanie'">Meanie</template>
<template [ngSwitchWhen]="'Miney'">Miney</template>
<template [ngSwitchWhen]="'Moe'">Moe</template>
<template ngSwitchDefault>Other</template>
</span>
<!-- #enddocregion NgSwitch -->
</div>
<div *ngIf="toeChoice == null">Pick a toe</div>
<div *ngIf="toeChoice != null">
You picked ...
<!-- #docregion NgSwitch, NgSwitch-expanded -->
<span [ngSwitch]="toeChoice">
<!-- #enddocregion NgSwitch -->
<!-- with *NgSwitch -->
<!-- #docregion NgSwitch -->
<span *ngSwitchWhen="'Eenie'">Eenie</span>
<span *ngSwitchWhen="'Meanie'">Meanie</span>
<span *ngSwitchWhen="'Miney'">Miney</span>
<span *ngSwitchWhen="'Moe'">Moe</span>
<span *ngSwitchDefault>other</span>
<!-- #enddocregion NgSwitch -->
<!-- with <template> -->
<template [ngSwitchWhen]="'Eenie'"><span>Eenie</span></template>
<template [ngSwitchWhen]="'Meanie'"><span>Meanie</span></template>
<template [ngSwitchWhen]="'Miney'"><span>Miney</span></template>
<template [ngSwitchWhen]="'Moe'"><span>Moe</span></template>
<template ngSwitchDefault><span>other</span></template>
<!-- #docregion NgSwitch -->
</span>
<!-- #enddocregion NgSwitch, NgSwitch-expanded -->
</div>
</div>
<a class="to-toc" href="#toc">top</a>
<!-- NgFor binding -->
<hr><h2>NgFor Binding</h2>
<hr><h2 id="ngFor">NgFor Binding</h2>
<div class="box">
<!-- #docregion NgFor-1 -->
@@ -470,52 +527,126 @@ bindon-ngModel
<hero-detail *ngFor="#hero of heroes" [hero]="hero"></hero-detail>
<!-- #enddocregion NgFor-2 -->
</div>
<br>
<a class="to-toc" href="#toc">top</a>
<h4 id="ngFor-index">NgFor with index</h4>
<p>with <i>semi-colon</i> separator</p>
<div class="box">
<!-- Ex: 1 - Hercules Son of Zeus -->
<!-- #docregion NgFor-3 -->
<div *ngFor="#hero of heroes, #i=index">{{i + 1}} - {{hero.fullName}}</div>
<div *ngFor="#hero of heroes; #i=index">{{i + 1}} - {{hero.fullName}}</div>
<!-- #enddocregion NgFor-3 -->
</div>
<br>
<p>with <i>comma</i> separator</p>
<div class="box">
<!-- Ex: "1 - Hercules Son of Zeus"" -->
<div *ngFor="#hero of heroes, #i=index">{{i + 1}} - {{hero.fullName}}</div>
</div>
<a class="to-toc" href="#toc">top</a>
<h4 id="ngFor-trackBy">NgForTrackBy</h4>
<button (click)="refreshHeroes()">Refresh heroes</button>
<p>First hero: <input [(ngModel)]="heroes[0].firstName"></p>
<p><i>without</i> trackBy</p>
<div #noTrackBy class="box">
<!-- #docregion NgForTrackBy-1 -->
<div *ngFor="#hero of heroes">({{hero.id}}) {{hero.fullName}}</div>
<!-- #enddocregion NgForTrackBy-1 -->
</div>
<div id="noTrackByCnt" *ngIf="heroesNoTrackByChangeCount != 0" style="background-color:bisque">
Hero DOM elements change #<span style="background-color:gold">{{heroesNoTrackByChangeCount}}</span> without trackBy
</div>
<p>with trackBy and <i>semi-colon</i> separator</p>
<div #withTrackBy class="box">
<!-- #docregion NgForTrackBy-2 -->
<div *ngFor="#hero of heroes; trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
<!-- #enddocregion NgForTrackBy-2 -->
</div>
<div id="withTrackByCnt" *ngIf="heroesWithTrackByChangeCount != 0" style="background-color:bisque">
Hero DOM elements change #<span style="background-color:gold">{{heroesWithTrackByChangeCount}}</span> with trackBy
</div>
<p>with trackBy and <i>comma</i> separator</p>
<div class="box">
<div *ngFor="#hero of heroes, trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
</div>
<p>with trackBy and <i>space</i> separator</p>
<div #withTrackBy class="box">
<div *ngFor="#hero of heroes trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
</div>
<p>with <i>*ngForTrackBy</i></p>
<div class="box">
<!-- #docregion NgForTrackBy-2 -->
<div *ngFor="#hero of heroes" *ngForTrackBy="trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
<!-- #enddocregion NgForTrackBy-2 -->
</div>
<p>with <i>generic</i> trackById function</p>
<div class="box">
<!-- #docregion NgForTrackBy-3 -->
<div *ngFor="#hero of heroes" *ngForTrackBy="trackById">({{hero.id}}) {{hero.fullName}}</div>
<!-- #enddocregion NgForTrackBy-3 -->
</div>
<a class="to-toc" href="#toc">top</a>
<!-- * and template -->
<hr><h2>* and Template</h2>
<hr><h2 id="star-prefix">* prefix and &lt;template&gt;</h2>
<h3>NgIf expansion</h3>
<h3>*ngIf expansion</h3>
<p><i>*ngIf</i></p>
<!-- #docregion Template-1 -->
<hero-detail *ngIf="currentHero != null" [hero]="currentHero"></hero-detail>
<!-- #enddocregion Template-1 -->
<p><i>expand to template = "..."</i></p>
<!-- #docregion Template-2a -->
<hero-detail template="ngIf:currentHero != null" [hero]="currentHero"></hero-detail>
<!-- #enddocregion Template-2a -->
<p><i>expand to &lt;template&gt;</i></p>
<!-- #docregion Template-2 -->
<template [ngIf]="currentHero != null">
<hero-detail [hero]="currentHero"></hero-detail>
</template>
<!-- #enddocregion Template-2 -->
<h3>NgFor expansion</h3>
<h3>*ngFor expansion</h3>
<p><i>*ngFor</i></p>
<!-- *ngFor w/ hero-detail Component -->
<!-- #docregion Template-3a -->
<hero-detail *ngFor="#hero of heroes; trackBy:trackByHeroes" [hero]="hero"></hero-detail>
<!-- #enddocregion Template-3a -->
<p><i>expand to template = "..."</i></p>
<div class="box">
<!-- ngFor w/ hero-detail Component and a template "attribute" directive -->
<!-- *ngFor w/ hero-detail Component and a template "attribute" directive -->
<!-- #docregion Template-3 -->
<hero-detail template="ngFor #hero of heroes" [hero]="hero"></hero-detail>
<hero-detail template="ngFor #hero of heroes; trackBy:trackByHeroes" [hero]="hero"></hero-detail>
<!-- #enddocregion Template-3 -->
</div>
<br>
<p><i>expand to &lt;template&gt;</i></p>
<div class="box">
<!-- ngFor w/ hero-detail Component inside a template element -->
<!-- #docregion Template-4 -->
<template ngFor #hero [ngForOf]="heroes">
<template ngFor #hero [ngForOf]="heroes" [ngForTrackBy]="trackByHeroes>
<hero-detail [hero]="hero"></hero-detail>
</template>
<!-- #enddocregion Template-4 -->
</div>
<a class="to-toc" href="#toc">top</a>
<!-- template local variable -->
<hr><h2>Template local variables</h2>
<hr><h2 id="local-vars">Template local variables</h2>
<!-- #docregion var-phone -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
@@ -547,8 +678,10 @@ bindon-ngModel
<!-- btn refers to the button element; show its disabled state -->
<button #btn disabled [textContent]="'disabled by attribute: ' + btn.disabled.toString()"></button>
<a class="to-toc" href="#toc">top</a>
<!-- inputs and output -->
<hr><h2>Inputs and Outputs</h2>
<hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2>
<!-- #docregion io-1 -->
<img [src]="iconUrl"/>
@@ -560,11 +693,13 @@ bindon-ngModel
</hero-detail>
<!-- #enddocregion io-2 -->
<div myClick2 (myClick)="clickMessage2=$event">myClick2</div>
<div (myClick)="clickMessage2=$event">myClick2</div>
{{clickMessage2}}
<a class="to-toc" href="#toc">top</a>
<!-- Pipes -->
<hr><h2>Pipes</h2>
<hr><h2 id="pipes">Pipes</h2>
<!-- #docregion pipes-1 -->
<!-- Force title to uppercase -->
@@ -596,9 +731,10 @@ bindon-ngModel
<label>Price: </label>{{product['price'] | currency:'$'}}
</div>
<a class="to-toc" href="#toc">top</a>
<!-- Null values and the Elvis operator -->
<hr><h2>Elvis</h2>
<hr><h2 id="elvis">Elvis <i>?.</i></h2>
<div>
<!-- #docregion elvis-1 -->
@@ -645,8 +781,16 @@ The null hero's name is {{nullHero.firstName}}
<!-- Todo: discuss this in the Style binding section -->
<!-- enums in bindings -->
<!--
<hr><h2>Enums in binding</h2>
<hr><h2 id="enums">Enums in binding</h2>
<p>The current color number is {{color}}</p>
<p><button [style.color]="color.toString()" (click)="colorToggle()">Enum Toggle</button>
<a class="to-toc" href="#toc">top</a>
-->
<!-- #docregion my-first-app -->
<h3>My First Angular Application</h3>
<!-- #enddocregion my-first-app -->
<a class="to-toc" href="#toc">top</a>
@@ -8,4 +8,5 @@ img {height: 100px;}
.bad {color: red;}
.curly {font-family: "Brush Script MT"}
.toe {margin-left: 1em; font-style: italic;}
little-hero {color:blue; font-size: smaller; background-color: Turquoise }
little-hero {color:blue; font-size: smaller; background-color: Turquoise }
.to-toc {margin-top: 10px; display: block}
@@ -1,9 +1,44 @@
<!-- #docregion my-first-app -->
<h3>My First Angular Application</h3>
<!-- #enddocregion my-first-app -->
<!-- #docplaster -->
<a id="toc"></a>
<h1>Template Syntax</h1>
<a href="#interpolation">Interpolation</a><br>
<a href="#mental-model">Mental Model</a><br>
<a href="#buttons">Buttons</a><br>
<a href="#prop-vs-attrib">Properties vs. Attributes</a><br>
<br>
<a href="#property-binding">Property Binding</a><br>
<div style="margin-left:8px">
<a href="#attribute-binding">Attribute Binding</a><br>
<a href="#class-binding">Class Binding</a><br>
<a href="#style-binding">Style Binding</a><br>
</div>
<br>
<a href="#event-binding">Event Binding</a><br>
<br>
<div>Directives</div>
<div style="margin-left:8px">
<a href="#ngModel">NgModel (two-way) Binding</a><br>
<a href="#ngClass">NgClass Binding</a><br>
<a href="#ngStyle">NgStyle Binding</a><br>
<a href="#ngIf">NgIf</a><br>
<a href="#ngSwitch">NgSwitch</a><br>
<a href="#ngFor">NgFor</a><br>
<div style="margin-left:8px">
<a href="#ngFor-index">NgFor with index</a><br>
<a href="#ngFor-trackBy">NgFor with trackBy</a><br>
</div>
</div>
<br>
<a href="#star-prefix">* prefix and &lt;template&gt;</a><br>
<a href="#local-vars">Template local variables</a><br>
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
<a href="#pipes">Pipes</a><br>
<a href="#elvis">Elvis <i>?.</i></a><br>
<a href="#enums">Enums</a><br>
<!-- Interpolation and expressions -->
<hr><h2>Interpolation</h2>
<hr><h2 id="interpolation">Interpolation</h2>
<!-- #docregion first-interpolation -->
<p>My current hero is {{currentHero.firstName}}</p>
@@ -26,9 +61,10 @@
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
<!-- #enddocregion sum-2 -->
<a class="to-toc" href="#toc">top</a>
<!-- New Mental Model -->
<hr><h2>New Mental Model</h2>
<hr><h2 id="mental-model">New Mental Model</h2>
<!--<img src="http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png">-->
<!-- Public Domain terms of use: http://www.wpclipart.com/terms.html -->
@@ -65,11 +101,10 @@
</div>
<br><br>
<!-- See https://github.com/angular/angular/issues/5707 about "myClick (myClick)" -->
<!-- #docregion event-binding-syntax-1 -->
<button (click) = "onSave()">Save</button>
<hero-detail (deleted)="onHeroDeleted()"></hero-detail>
<div myClick (myClick)="clicked=$event">click me</div>
<div (myClick)="clicked=$event">click me</div>
<!-- #enddocregion event-binding-syntax-1 -->
{{clicked}}
<br><br>
@@ -97,8 +132,10 @@
<!-- #enddocregion style-binding-syntax-1 -->
button</button>
<a class="to-toc" href="#toc">top</a>
<!-- property vs. attribute -->
<hr><h2>Property vs. Attribute (img examples)</h2>
<hr><h2 id="prop-vs-attrib">Property vs. Attribute (img examples)</h2>
<!-- examine the following <img> tag in the browser tools -->
<img src="images/ng-logo.png"
[src]="heroImageUrl">
@@ -109,10 +146,10 @@ button</button>
<img bind-src="heroImageUrl"/>
<img [attr.src]="villainImageUrl"/>
<a class="to-toc" href="#toc">top</a>
<!-- buttons -->
<hr><h2>Buttons</h2>
<hr><h2 id="buttons">Buttons</h2>
<button>Enabled (but does nothing)</button>
<button disabled>Disabled</button>
@@ -124,9 +161,10 @@ button</button>
<button bind-disabled="isUnchanged" on-click="onSave($event)">Disabled Cancel</button>
<button [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button>
<a class="to-toc" href="#toc">top</a>
<!-- property binding -->
<hr><h2>Property Binding</h2>
<hr><h2 id="property-binding">Property Binding</h2>
<!-- #docregion property-binding-1 -->
<img [src]="heroImageUrl">
@@ -157,8 +195,10 @@ button</button>
<div [textContent]="'The title is '+title"></div>
<!-- #enddocregion property-binding-vs-interpolation -->
<a class="to-toc" href="#toc">top</a>
<!-- attribute binding -->
<hr><h2>Attribute Binding</h2>
<hr><h2 id="attribute-binding">Attribute Binding</h2>
<!-- create and set a colspan attribute -->
<!-- #docregion attrib-binding-colspan -->
@@ -195,12 +235,10 @@ button</button>
<button disabled [disabled]="false">Enabled (but inert)</button>
</div>
<a class="to-toc" href="#toc">top</a>
<!-- class binding -->
<hr><h2>Class Binding</h2>
<hr><h2 id="class-binding">Class Binding</h2>
<!-- #docregion class-binding-1 -->
<!-- standard class attribute setting -->
@@ -226,10 +264,10 @@ button</button>
<div bind-class.special="isSpecial">This class binding is special too</div>
<a class="to-toc" href="#toc">top</a>
<!--style binding -->
<hr><h2>Style Binding</h2>
<hr><h2 id="style-binding">Style Binding</h2>
<!-- #docregion style-binding-1 -->
<button [style.color] = "isSpecial ? 'red' : 'green'">Red</button>
@@ -241,8 +279,10 @@ button</button>
<button [style.fontSize.%]="!isSpecial ? 150 : 50" >Small</button>
<!-- #enddocregion style-binding-2 -->
<a class="to-toc" href="#toc">top</a>
<!-- event binding -->
<hr><h2>Event Binding</h2>
<hr><h2 id="event-binding">Event Binding</h2>
<!-- #docregion event-binding-1 -->
<button (click)="onSave()">Save</button>
@@ -257,7 +297,7 @@ button</button>
<!-- `myClick` is an event on the custom `MyClickDirective` -->
<!-- #docregion my-click -->
<div myClick (myClick)="clickMessage=$event">click with myClick</div>
<div (myClick)="clickMessage=$event">click with myClick</div>
<!-- #enddocregion my-click -->
<!-- #enddocregion event-binding-3 -->
{{clickMessage}}
@@ -298,10 +338,11 @@ button</button>
<!-- #enddocregion event-binding-propagation -->
<br><br>
<a class="to-toc" href="#toc">top</a>
<!-- Two way data binding unwound;
passing the changed display value to the event handler via `$event` -->
<hr><h2>NgModel Binding</h2>
<hr><h2 id="ngModel">NgModel (two-way) Binding</h2>
<h3>Result: {{currentHero.firstName}}</h3>
@@ -336,10 +377,10 @@ bindon-ngModel
(ngModelChange) = "setUpperCaseFirstName($event)"
<br>
<a class="to-toc" href="#toc">top</a>
<!-- NgClass binding -->
<hr><h2>NgClass Binding</h2>
<hr><h2 id="ngClass">NgClass Binding</h2>
<p>setClasses returns {{setClasses() | json}}</p>
<!-- #docregion NgClass-1 -->
@@ -356,9 +397,10 @@ After setClasses(), the classes are "{{classDiv.className}}"
<div class="bad curly special">Bad curly special</div>
<div [ngClass]="{bad:false, curly:true, special:true}">Curly special</div>
<a class="to-toc" href="#toc">top</a>
<!-- NgStyle binding -->
<hr><h2>NgStyle Binding</h2>
<hr><h2 id="ngStyle">NgStyle Binding</h2>
<!-- #docregion NgStyle-1 -->
<div [style.fontSize]="isSpecial ? 'x-large' : 'smaller'" >
@@ -381,10 +423,10 @@ After setClasses(), the classes are "{{classDiv.className}}"
<!-- not used in chapter -->
<a class="to-toc" href="#toc">top</a>
<!-- NgIf binding -->
<hr><h2>NgIf Binding</h2>
<hr><h2 id="ngIf">NgIf Binding</h2>
<!-- #docregion NgIf-1 -->
<div *ngIf="currentHero">Hello, {{currentHero.firstName}}</div>
@@ -399,7 +441,6 @@ After setClasses(), the classes are "{{classDiv.className}}"
<hero-detail *ngIf="isActive"></hero-detail>
<!-- #enddocregion NgIf-2 -->
<!-- NgIf binding with template (no *) -->
<template [ngIf]="currentHero">Add {{currentHero.firstName}} with template</template>
@@ -422,10 +463,10 @@ After setClasses(), the classes are "{{classDiv.className}}"
<div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
<!-- #enddocregion NgIf-3 -->
<a class="to-toc" href="#toc">top</a>
<!-- NgSwitch binding -->
<hr><h2>NgSwitch Binding</h2>
<hr><h2 id="ngSwitch">NgSwitch Binding</h2>
<fieldset #toePicker (click)="toeChooser(toePicker)" >
<input type="radio" name="toes" value="Eenie">Eenie
@@ -437,22 +478,38 @@ After setClasses(), the classes are "{{classDiv.className}}"
<div class="toe">
<div *ngIf="!toeChoice">Pick a toe</div>
<div *ngIf="toeChoice">You picked
<!-- #docregion NgSwitch -->
<div *ngIf="toeChoice">
You picked ...
<!-- #docregion NgSwitch, NgSwitch-expanded -->
<span [ngSwitch]="toeChoice">
<template [ngSwitchWhen]="'Eenie'">Eenie</template>
<template [ngSwitchWhen]="'Meanie'">Meanie</template>
<template [ngSwitchWhen]="'Miney'">Miney</template>
<template [ngSwitchWhen]="'Moe'">Moe</template>
<template ngSwitchDefault>Other</template>
</span>
<!-- #enddocregion NgSwitch -->
</div>
<!-- #enddocregion NgSwitch -->
<!-- with *NgSwitch -->
<!-- #docregion NgSwitch -->
<span *ngSwitchWhen="'Eenie'">Eenie</span>
<span *ngSwitchWhen="'Meanie'">Meanie</span>
<span *ngSwitchWhen="'Miney'">Miney</span>
<span *ngSwitchWhen="'Moe'">Moe</span>
<span *ngSwitchDefault>other</span>
<!-- #enddocregion NgSwitch -->
<!-- with <template> -->
<template [ngSwitchWhen]="'Eenie'"><span>Eenie</span></template>
<template [ngSwitchWhen]="'Meanie'"><span>Meanie</span></template>
<template [ngSwitchWhen]="'Miney'"><span>Miney</span></template>
<template [ngSwitchWhen]="'Moe'"><span>Moe</span></template>
<template ngSwitchDefault><span>other</span></template>
<!-- #docregion NgSwitch -->
</span>
<!-- #enddocregion NgSwitch, NgSwitch-expanded -->
</div>
</div>
<a class="to-toc" href="#toc">top</a>
<!-- NgFor binding -->
<hr><h2>NgFor Binding</h2>
<hr><h2 id="ngFor">NgFor Binding</h2>
<div class="box">
<!-- #docregion NgFor-1 -->
@@ -467,52 +524,125 @@ After setClasses(), the classes are "{{classDiv.className}}"
<hero-detail *ngFor="#hero of heroes" [hero]="hero"></hero-detail>
<!-- #enddocregion NgFor-2 -->
</div>
<br>
<a class="to-toc" href="#toc">top</a>
<h4 id="ngFor-index">NgFor with index</h4>
<p>with <i>semi-colon</i> separator</p>
<div class="box">
<!-- Ex: 1 - Hercules Son of Zeus -->
<!-- #docregion NgFor-3 -->
<div *ngFor="#hero of heroes, #i=index">{{i + 1}} - {{hero.fullName}}</div>
<div *ngFor="#hero of heroes; #i=index">{{i + 1}} - {{hero.fullName}}</div>
<!-- #enddocregion NgFor-3 -->
</div>
<br>
<p>with <i>comma</i> separator</p>
<div class="box">
<!-- Ex: "1 - Hercules Son of Zeus"" -->
<div *ngFor="#hero of heroes, #i=index">{{i + 1}} - {{hero.fullName}}</div>
</div>
<a class="to-toc" href="#toc">top</a>
<h4 id="ngFor-trackBy">NgForTrackBy</h4>
<button (click)="refreshHeroes()">Refresh heroes</button>
<p>First hero: <input [(ngModel)]="heroes[0].firstName"></p>
<p><i>without</i> trackBy</p>
<div #noTrackBy class="box">
<!-- #docregion NgForTrackBy-1 -->
<div *ngFor="#hero of heroes">({{hero.id}}) {{hero.fullName}}</div>
<!-- #enddocregion NgForTrackBy-1 -->
</div>
<div id="noTrackByCnt" *ngIf="heroesNoTrackByChangeCount" style="background-color:bisque">
Hero DOM elements change #<span style="background-color:gold">{{heroesNoTrackByChangeCount}}</span> without trackBy
</div>
<p>with trackBy and <i>semi-colon</i> separator</p>
<div #withTrackBy class="box">
<!-- #docregion NgForTrackBy-2 -->
<div *ngFor="#hero of heroes; trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
<!-- #enddocregion NgForTrackBy-2 -->
</div>
<div id="withTrackByCnt" *ngIf="heroesWithTrackByChangeCount" style="background-color:bisque">
Hero DOM elements change #<span style="background-color:gold">{{heroesWithTrackByChangeCount}}</span> with trackBy
</div>
<p>with trackBy and <i>comma</i> separator</p>
<div class="box">
<div *ngFor="#hero of heroes, trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
</div>
<p>with trackBy and <i>space</i> separator</p>
<div #withTrackBy class="box">
<div *ngFor="#hero of heroes trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
</div>
<p>with <i>*ngForTrackBy</i></p>
<div class="box">
<!-- #docregion NgForTrackBy-2 -->
<div *ngFor="#hero of heroes" *ngForTrackBy="trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
<!-- #enddocregion NgForTrackBy-2 -->
</div>
<p>with <i>generic</i> trackById function</p>
<div class="box">
<!-- #docregion NgForTrackBy-3 -->
<div *ngFor="#hero of heroes" *ngForTrackBy="trackById">({{hero.id}}) {{hero.fullName}}</div>
<!-- #enddocregion NgForTrackBy-3 -->
</div>
<a class="to-toc" href="#toc">top</a>
<!-- * and template -->
<hr><h2>* and Template</h2>
<hr><h2 id="star-prefix">* prefix and &lt;template&gt;</h2>
<h3>NgIf expansion</h3>
<h3>*ngIf expansion</h3>
<p><i>*ngIf</i></p>
<!-- #docregion Template-1 -->
<hero-detail *ngIf="currentHero" [hero]="currentHero"></hero-detail>
<!-- #enddocregion Template-1 -->
<p><i>expand to template = "..."</i></p>
<!-- #docregion Template-2a -->
<hero-detail template="ngIf:currentHero" [hero]="currentHero"></hero-detail>
<!-- #enddocregion Template-2a -->
<p><i>expand to &lt;template&gt;</i></p>
<!-- #docregion Template-2 -->
<template [ngIf]="currentHero">
<hero-detail [hero]="currentHero"></hero-detail>
</template>
<!-- #enddocregion Template-2 -->
<h3>NgFor expansion</h3>
<h3>*ngFor expansion</h3>
<p><i>*ngFor</i></p>
<!-- *ngFor w/ hero-detail Component -->
<!-- #docregion Template-3a -->
<hero-detail *ngFor="#hero of heroes; trackBy:trackByHeroes" [hero]="hero"></hero-detail>
<!-- #enddocregion Template-3a -->
<p><i>expand to template = "..."</i></p>
<div class="box">
<!-- ngFor w/ hero-detail Component and a template "attribute" directive -->
<!-- #docregion Template-3 -->
<hero-detail template="ngFor #hero of heroes" [hero]="hero"></hero-detail>
<hero-detail template="ngFor #hero of heroes; trackBy:trackByHeroes" [hero]="hero"></hero-detail>
<!-- #enddocregion Template-3 -->
</div>
<br>
<p><i>expand to &lt;template&gt;</i></p>
<div class="box">
<!-- ngFor w/ hero-detail Component inside a template element -->
<!-- #docregion Template-4 -->
<template ngFor #hero [ngForOf]="heroes">
<template ngFor #hero [ngForOf]="heroes" [ngForTrackBy]="trackByHeroes">
<hero-detail [hero]="hero"></hero-detail>
</template>
<!-- #enddocregion Template-4 -->
</div>
<a class="to-toc" href="#toc">top</a>
<!-- template local variable -->
<hr><h2>Template local variables</h2>
<hr><h2 id="local-vars">Template local variables</h2>
<!-- #docregion var-phone -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
@@ -544,8 +674,10 @@ After setClasses(), the classes are "{{classDiv.className}}"
<!-- btn refers to the button element; show its disabled state -->
<button #btn disabled [textContent]="'disabled by attribute: '+btn.disabled"></button>
<a class="to-toc" href="#toc">top</a>
<!-- inputs and output -->
<hr><h2>Inputs and Outputs</h2>
<hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2>
<!-- #docregion io-1 -->
<img [src]="iconUrl"/>
@@ -557,11 +689,13 @@ After setClasses(), the classes are "{{classDiv.className}}"
</hero-detail>
<!-- #enddocregion io-2 -->
<div myClick2 (myClick)="clickMessage2=$event">myClick2</div>
<div (myClick)="clickMessage2=$event">myClick2</div>
{{clickMessage2}}
<a class="to-toc" href="#toc">top</a>
<!-- Pipes -->
<hr><h2>Pipes</h2>
<hr><h2 id="pipes">Pipes</h2>
<!-- #docregion pipes-1 -->
<!-- Force title to uppercase -->
@@ -597,9 +731,10 @@ After setClasses(), the classes are "{{classDiv.className}}"
<label>Price: </label>{{product.price | currency:'USD':true}}
</div>
<a class="to-toc" href="#toc">top</a>
<!-- Null values and the Elvis operator -->
<hr><h2>Elvis</h2>
<hr><h2 id="elvis">Elvis <i>?.</i></h2>
<div>
<!-- #docregion elvis-1 -->
@@ -646,10 +781,20 @@ The null hero's name is {{nullHero?.firstName}}
</div>
<a class="to-toc" href="#toc">top</a>
<!-- Todo: discuss this in the Style binding section -->
<!-- enums in bindings -->
<hr><h2>Enums in binding</h2>
<hr><h2 id="enums">Enums in binding</h2>
<p>The name of the Color.Red enum is {{Color[Color.Red]}}</p>
<p>The current color number is {{color}}</p>
<p><button [style.color]="Color[color]" (click)="colorToggle()">Enum Toggle</button>
<a class="to-toc" href="#toc">top</a>
<!-- #docregion my-first-app -->
<h3>My First Angular Application</h3>
<!-- #enddocregion my-first-app -->
<a class="to-toc" href="#toc">top</a>
@@ -1,6 +1,6 @@
//#docplaster
import {Component} from 'angular2/core';
import {Component, AfterViewInit, ElementRef, OnInit, QueryList, ViewChildren} from 'angular2/core';
import {NgForm} from 'angular2/common';
import {Hero} from './hero';
@@ -25,7 +25,15 @@ export enum Color {Red, Green, Blue};
MyClickDirective, MyClickDirective2
]
})
export class AppComponent {
export class AppComponent implements AfterViewInit, OnInit {
ngOnInit(){
this.refreshHeroes();
}
ngAfterViewInit() {
this._detectNgForTrackByEffects();
}
actionName = 'Go for it';
alert = alerter;
@@ -56,7 +64,7 @@ export class AppComponent {
getVal() {return this.val};
heroes = Hero.MockHeroes;
heroes:Hero[];
// heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
// Public Domain terms of use: http://www.wpclipart.com/terms.html
@@ -101,6 +109,26 @@ export class AppComponent {
price: 42
};
// #docregion refresh-heroes
// update this.heroes with fresh set of cloned heroes
refreshHeroes() {
this.heroes = Hero.MockHeroes.map(hero => Hero.clone(hero));
}
// #enddocregion refresh-heroes
// #docregion same-as-it-ever-was
private _samenessCount = 5;
moreOfTheSame() {this._samenessCount++;};
get sameAsItEverWas() {
var result:string[] = Array(this._samenessCount);
for (var i=result.length; i-- > 0;){result[i]='same as it ever was ...'}
return result;
// return [1,2,3,4,5].map(id => {
// return {id:id, text: 'same as it ever was ...'};
// });
}
// #enddocregion same-as-it-ever-was
setUpperCaseFirstName(firstName:string){
//console.log(firstName);
this.currentHero.firstName = firstName.toUpperCase();
@@ -153,10 +181,62 @@ export class AppComponent {
}
}
title = 'Template Syntax'
title = 'Template Syntax';
// #docregion trackByHeroes
trackByHeroes(index: number, hero: Hero) { return hero.id; }
// #enddocregion trackByHeroes
// #docregion trackById
trackById(index: number, item: any): string { return item['id']; }
// #enddocregion trackById
val=2;
// villainImageUrl = 'http://www.clker.com/cliparts/u/s/y/L/x/9/villain-man-hi.png'
// Public Domain terms of use http://www.clker.com/disclaimer.html
villainImageUrl = 'images/villain.png'
//////// Detect effects of NgForTrackBy ///////////////
@ViewChildren('noTrackBy') childrenNoTrackBy:QueryList<ElementRef>;
@ViewChildren('withTrackBy') childrenWithTrackBy:QueryList<ElementRef>;
private _oldNoTrackBy:HTMLElement[];
private _oldWithTrackBy:HTMLElement[];
heroesNoTrackByChangeCount = 0;
heroesWithTrackByChangeCount = 0;
private _detectNgForTrackByEffects() {
this._oldNoTrackBy = toArray(this.childrenNoTrackBy);
this._oldWithTrackBy = toArray(this.childrenWithTrackBy);
this.childrenNoTrackBy.changes.subscribe((changes:any) => {
let newNoTrackBy = toArray(changes);
let isSame = this._oldNoTrackBy.every((v:any, i:number) => v === newNoTrackBy[i]);
if (!isSame) {
this._oldNoTrackBy = newNoTrackBy;
this.heroesNoTrackByChangeCount++;
}
})
this.childrenWithTrackBy.changes.subscribe((changes:any) => {
let newWithTrackBy = toArray(changes);
let isSame = this._oldWithTrackBy.every((v:any, i:number) => v === newWithTrackBy[i]);
if (!isSame) {
this._oldWithTrackBy = newWithTrackBy;
this.heroesWithTrackByChangeCount++;
}
})
}
///////////////////
}
// helper to convert viewChildren to an array of HTMLElements
function toArray(viewChildren:QueryList<ElementRef>) {
let result: HTMLElement[] = [];
let children = viewChildren.toArray()[0].nativeElement.children;
for (var i = 0; i < children.length; i++) { result.push(children[i]); }
return result;
}
@@ -6,10 +6,15 @@ export class Hero {
public lastName?:string,
public birthdate?:Date,
public url?:string,
public rate:number = 100) {
this.id = Hero.nextId++;
public rate:number = 100,
id?:number) {
this.id = id != null ? id : Hero.nextId++;
}
static clone({firstName, lastName, birthdate, url, rate, id} : Hero){
return new Hero (firstName, lastName, birthdate, url, rate, id );
}
get fullName() {return `${this.firstName} ${this.lastName}`;}
static nextId = 1;
@@ -8,4 +8,5 @@ img {height: 100px;}
.bad {color: red;}
.curly {font-family: "Brush Script MT"}
.toe {margin-left: 1em; font-style: italic;}
little-hero {color:blue; font-size: smaller; background-color: Turquoise }
little-hero {color:blue; font-size: smaller; background-color: Turquoise }
.to-toc {margin-top: 10px; display: block}