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

View File

@ -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++;
}
})
}
*/
///////////////////
}

View File

@ -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>

View File

@ -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}

View File

@ -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>

View File

@ -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;
}

View File

@ -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;

View File

@ -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}

View File

@ -285,10 +285,17 @@ table
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'directives-ngFor-6')
+makeExample('template-syntax/dart/lib/app_component.html', 'NgFor-3')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'directives-ngFor-7')
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'directives-ngForTrackBy-1')
+makeExample('template-syntax/dart/lib/app_component.dart', 'trackByHeroes')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'directives-ngForTrackBy-2')
+makeExample('template-syntax/dart/lib/app_component.html', 'NgForTrackBy-2')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'directives-ngForTrackBy-3')
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template')
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-1')
+makeExample('template-syntax/dart/lib/app_component.html', 'Template-1')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-2a')
+makeExample('template-syntax/dart/lib/app_component.html', 'Template-2a')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-2')
+makeExample('template-syntax/dart/lib/app_component.html', 'Template-2')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-3')
@ -299,8 +306,11 @@ table
Dont make the mistake of writing `ngIf="currentHero"`!
That syntax assigns the *string* value "currentHero" to `ngIf`,
which won't work because `ngIf` expects a bool. **[QUESTION: Did I get that right?]**
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngSwitch-1')
+makeExample('template-syntax/dart/lib/app_component.html', 'NgSwitch-expanded')
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngSwitch-2')
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngFor-1')
+makeExample('template-syntax/dart/lib/app_component.html', 'NgFor-2')(format=".")
+makeExample('template-syntax/dart/lib/app_component.html', 'Template-3a')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngFor-2')
+makeExample('template-syntax/dart/lib/app_component.html', 'Template-3')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngFor-3')

View File

@ -18,6 +18,11 @@ include ../../../../_includes/_util-fns
* [Event binding](#event-binding)
* [Two-way data binding with `NgModel`](#ngModel)
* [Built-in directives](#directives)
* [NgClass](#ngClass)
* [NgStyle](#ngStyle)
* [NgIf](#ngIf)
* [NgSwitch](#ngSwitch)
* [NgFor](#ngFor)
* [* and &lt;template>](#star-template)
* [Local template variables](#local-vars)
* [Input and output properties](#inputs-outputs)
@ -1078,18 +1083,35 @@ figure.image-display
+makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch')(format=".")
// #docregion directives-ngSwitch-2
:marked
We bind the parent `NgSwitch` directive to an expression returning a *switch value*. The value is a string in this example, but it can be a value of any type.
We bind the parent `NgSwitch` directive to an expression returning a *switch value*.
The value is a string in this example, but it can be a value of any type.
The parent `NgSwitch` directive controls a set of child`<template>` elements. Each `<template>` wraps a candidate subtree. A template is either pegged to a “match value” expression or marked as the default template.
In this example, the parent `NgSwitch` directive controls a set of child `<span>` elements.
A `<span>` is either pegged to a *match value* expression or marked as the default.
**At any particular moment, at most one of these templates is in the DOM.**
If the templates *match value* equals the switch value, Angular adds the templates subtree to the DOM. If no template is a match and there is a default template, Angular adds the default templates subtree to the DOM. Angular removes and destroys the subtrees of all other templates.
**At any particular moment, at most one of these *spans* is in the DOM.**
If the *span*s *match value* equals the switch value, Angular adds the `<span>` to the DOM.
If none of the *spans* is a match, Angular adds the default *span* to the DOM.
Angular removes and destroys all other *spans*.
.l-sub-section
:marked
We could substitute any element for the *span* in this example.
That element could be a `<div>` with a vast subtree of its own elements.
Only the matching `<div>` and its subtree would appear in the DOM;
the others would be removed.
:marked
Three collaborating directives are at work here:
1. `ngSwitch`: bound to an expression that returns the switch value
1. `ngSwitchWhen`: bound to an expression returning a match value
1. `ngSwitchDefault`: a marker attribute on the default template
1. `ngSwitchDefault`: a marker attribute on the default element
.alert.is-critical
:marked
**Do *not*** put the asterisk (`*`) in front of `ngSwitch`. Use the property binding instead.
**Do** put the asterisk (`*`) in front of `ngSwitchWhen` and `ngSwitchDefault`.
For more information, see [\* and &lt;template>](#star-template).
// #enddocregion directives-ngSwitch-2
// #docregion directives-ngFor-1
@ -1155,56 +1177,90 @@ figure.image-display
The next example captures the index in a variable named `i`, using it to stamp out rows like "1 - Hercules Son of Zeus".
// #enddocregion directives-ngFor-6
+makeExample('template-syntax/ts/app/app.component.html', 'NgFor-3')(format=".")
// #docregion directives-ngFor-7
.l-sub-section
:marked
Learn about other special values such as `last`, `even`, and `odd` in the [NgFor API reference](/docs/ts/latest/api/common/NgFor-directive.html).
Learn about other special *index-like* values such as `last`, `even`, and `odd` in the [NgFor API reference](/docs/ts/latest/api/common/NgFor-directive.html).
// #enddocregion directives-ngFor-7
// #docregion directives-ngForTrackBy-1
:marked
#### NgForTrackBy
The `ngFor` directive has the potential to perform poorly, especially with large lists.
A small change to one item, an item removed, or an item added can trigger a cascade of DOM manipulations.
For example, we could refresh the list of heroes by re-querying the server.
The refreshed list probably contains most, if not all, of the previously displayed heroes.
*We* know this because the `id` of each hero hasn't changed.
But Angular sees only a fresh list of new object references.
It has no choice but to tear down the old list, discard those DOM elements, and re-build a new list with new DOM elements.
Angular can avoid this churn if we give it a *tracking* function that tells it what we know:
that two objects with the same `hero.id` are the same *hero*. Here is such a function:
// #enddocregion directives-ngForTrackBy-1
+makeExample('template-syntax/ts/app/app.component.ts', 'trackByHeroes')(format=".")
// #docregion directives-ngForTrackBy-2
:marked
Now set the `NgForTrackBy` directive to that *tracking* function.
Angular offers a variety of equivalent syntax choices including these two:
// #enddocregion directives-ngForTrackBy-2
+makeExample('template-syntax/ts/app/app.component.html', 'NgForTrackBy-2')(format=".")
// #docregion directives-ngForTrackBy-3
:marked
The *tracking* function doesn't eliminate all DOM changes.
Angular may have to update the DOM element if the same-hero *properties* have changed.
But if the properties haven't changed &mdash; and most of the time they will not have changed &mdash;
Angular can leave those DOM elements alone. The list UI will be smoother and more responsive.
Here is an illustration of the `NgForTrackBy` effect.
figure.image-display
img(src='/resources/images/devguide/template-syntax/ng-for-track-by-anim.gif' alt="NgForTrackBy")
// #enddocregion directives-ngForTrackBy-3
// #docregion star-template
<a name="star-template"></a>
<a name="structural-directive"></a>
.l-main-section
:marked
## * and &lt;template>
When we reviewed the `NgFor` and `NgIf` built-in directives, we called out an oddity of the syntax: the asterisk (`*`) that appears before the directive name.
## * and &lt;template&gt;
When we reviewed the `NgFor`, `NgIf`, and `NgSwitch` built-in directives, we called out an oddity of the syntax: the asterisk (`*`) that appears before the directive names.
The `*` is a bit of syntactic sugar that makes it easier to read and write directives that modify HTML layout
with the help of templates.
`NgFor`, `NgIf`, and `NgSwitch` all add and remove element subtrees that are wrapped in `<template>` tags.
With the [NgSwitch](#ngSwitch) directive we always write the `<template>` tags explicitly.
There isnt much choice; we define a different template for each switch choice
and let the directive render the template that matches the switch value.
[NgFor](#ngFor) and [NgIf](#ngIf), on the other hand, each need only one template:
the *template-to-repeat* and the *template-to-include*, respectively.
The `*` prefix syntax is a convenient way to skip the `<template>` wrapper tags and
focus directly on the HTML element to repeat or include.
Angular sees the `*` and expands the HTML into the `<template>` tags for us.
We didn't see the `<template>` tags because the `*` prefix syntax allowed us to skip those tags and
focus directly on the HTML element that we are including, excluding, or repeating.
In this section we go under the hood and see how
Angular strips away the `*` and expands the HTML into the `<template>` tags for us.
// #enddocregion star-template
// #docregion star-template-ngIf-1
:marked
### Expanding `*ngIf`
We can do that expansion ourselves if we wish. Here's some code with `*ngIf`:
We can do what Angular does ourselves and expand the `*` prefix syntax to template syntax. Here's some code with `*ngIf`:
// #enddocregion star-template-ngIf-1
+makeExample('template-syntax/ts/app/app.component.html', 'Template-1')(format=".")
// #docregion star-template-ngIf-2a
:marked
The `currentHero` is referenced twice, first as the true/false condition for `NgIf` and
again as the actual hero passed into the `HeroDetailComponent`.
The first expansion step transports the `ngIf` (without the `*` prefix) and its contents
into an expression assigned to a `template` directive.
// #enddocregion star-template-ngIf-2a
+makeExample('template-syntax/ts/app/app.component.html', 'Template-2a')(format=".")
// #docregion star-template-ngIf-2
:marked
Here's the equivalent with `<template>` and `ngIf`:
The next (and final) step unfolds the HTML into a `<template>` tag and `[ngIf]` [property binding](#property-binding):
// #enddocregion star-template-ngIf-2
+makeExample('template-syntax/ts/app/app.component.html', 'Template-2')(format=".")
// #docregion star-template-ngIf-3
:marked
Notice that the `*` is gone and we have a [property binding](#property-binding) to the `ngIf`
directive, applied in this case to the `<template>` rather than
the applications `hero-detail` component.
The `[hero]="currentHero"` binding remains on the child `<hero-detail>`
Notice that the `[hero]="currentHero"` binding remains on the child `<hero-detail>`
element inside the template.
// #enddocregion star-template-ngIf-3
@ -1219,26 +1275,48 @@ figure.image-display
… even when there is no `currentHero`!
// #enddocregion star-template-ngIf-4
// #docregion star-template-ngSwitch-1
:marked
### Expanding `*ngSwitch`
A similar transformation applies to `*ngSwitch`. We can de-sugar the syntax ourselves.
Here's an example, first with `*ngSwitchWhen` and `*ngSwitchDefault` and then again with `<template>` tags:
// #enddocregion star-template-ngSwitch-1
+makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch-expanded')
// #docregion star-template-ngSwitch-2
:marked
The `*ngSwitchWhen` and `*ngSwitchDefault` expand in exactly the same manner as `*ngIf`,
wrapping their former elements in `<template>` tags.
Now we can see why the `ngSwitch` itself is not prefixed with an asterisk (*).
It does not define content. It's job is to control a collection of templates.
In this case, it governs two sets of `NgSwitchWhen` and `NgSwitchDefault` directives.
We should expect it to display the values of the selected template twice,
once for the (*) prefixed version and once for the expanded template version.
That's exactly what we see in this example:
figure.image-display
img(src='/resources/images/devguide/template-syntax/ng-switch-anim.gif' alt="NgSwitch")
// #enddocregion star-template-ngSwitch-2
// #docregion star-template-ngFor-1
:marked
### Expanding `*ngFor`
A similar transformation applies to `*ngFor`. We can de-sugar the syntax ourselves. First, here's an example with `*ngFor`:
The `*ngFor` undergoes a similar transformation. We begin with an `*ngFor` example:
// #enddocregion star-template-ngFor-1
+makeExample('template-syntax/ts/app/app.component.html', 'NgFor-2')(format=".")
+makeExample('template-syntax/ts/app/app.component.html', 'Template-3a')(format=".")
// #docregion star-template-ngFor-2
:marked
Here's the same example, slightly expanded:
Here's the same example after transporting the `ngFor` to the `template` directive:
// #enddocregion star-template-ngFor-2
+makeExample('template-syntax/ts/app/app.component.html', 'Template-3')(format=".")
// #docregion star-template-ngFor-3
:marked
And here it is, expanded further:
And here it is expanded further into a `<template>` tag wrapping the original `<hero-detail>` element:
// #enddocregion star-template-ngFor-3
+makeExample('template-syntax/ts/app/app.component.html', 'Template-4')(format=".")
// #docregion star-template-ngFor-4
:marked
The `NgFor` code is a bit more complex than `NgIf` because a repeater has more moving parts to configure.
In this case, we have to remember the `NgForOf` directive that identifies the list.
In this case, we have to remember to create and assign the `NgForOf` directive that identifies the list and the `NgForTrackBy` directive.
Using the `*ngFor` syntax is much easier than writing out this expanded HTML ourselves.
// #enddocregion star-template-ngFor-4

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB