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 curly'; // XXX: This isn't working.
String badCurly = 'bad'; // XXX: This isn't working. String badCurly = 'bad'; // XXX: This isn't working.
// List<String> badCurly = ['bad', 'curly']; // 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 canSave = true;
bool isActive = false; bool isActive = false;
bool isSpecial = true; bool isSpecial = true;
@ -143,6 +140,8 @@ class AppComponent {
} }
// #enddocregion setStyles // #enddocregion setStyles
String title = 'Template Syntax';
String toeChoice;
String toeChooser(Element picker) { String toeChooser(Element picker) {
List<Element> choices = picker.children; List<Element> choices = picker.children;
for (var i = 0; i < choices.length; i++) { 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 --> <!-- #docplaster -->
<h3>My First Angular Application</h3> <a id="toc"></a>
<!-- #enddocregion my-first-app --> <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 --> <!-- Interpolation and expressions -->
<hr><h2>Interpolation</h2> <hr><h2 id="interpolation">Interpolation</h2>
<!-- #docregion first-interpolation --> <!-- #docregion first-interpolation -->
<p>My current hero is {{currentHero.firstName}}</p> <p>My current hero is {{currentHero.firstName}}</p>
@ -26,9 +61,10 @@
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p> <p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
<!-- #enddocregion sum-2 --> <!-- #enddocregion sum-2 -->
<a class="to-toc" href="#toc">top</a>
<!-- New Mental Model --> <!-- 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">--> <!--<img src="http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png">-->
<!-- Public Domain terms of use: http://www.wpclipart.com/terms.html --> <!-- Public Domain terms of use: http://www.wpclipart.com/terms.html -->
@ -65,11 +101,10 @@
</div> </div>
<br><br> <br><br>
<!-- See https://github.com/angular/angular/issues/5707 about "myClick (myClick)" -->
<!-- #docregion event-binding-syntax-1 --> <!-- #docregion event-binding-syntax-1 -->
<button (click) = "onSave()">Save</button> <button (click) = "onSave()">Save</button>
<hero-detail (deleted)="onHeroDeleted()"></hero-detail> <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 --> <!-- #enddocregion event-binding-syntax-1 -->
{{clickity}} {{clickity}}
<br><br> <br><br>
@ -97,8 +132,10 @@
<!-- #enddocregion style-binding-syntax-1 --> <!-- #enddocregion style-binding-syntax-1 -->
button</button> button</button>
<a class="to-toc" href="#toc">top</a>
<!-- property vs. attribute --> <!-- 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 --> <!-- examine the following <img> tag in the browser tools -->
<img src="assets/images/ng-logo.png" <img src="assets/images/ng-logo.png"
[src]="heroImageUrl"> [src]="heroImageUrl">
@ -109,10 +146,10 @@ button</button>
<img bind-src="heroImageUrl"/> <img bind-src="heroImageUrl"/>
<img [attr.src]="villainImageUrl"/> <img [attr.src]="villainImageUrl"/>
<a class="to-toc" href="#toc">top</a>
<!-- buttons --> <!-- buttons -->
<hr><h2>Buttons</h2> <hr><h2 id="buttons">Buttons</h2>
<button>Enabled (but does nothing)</button> <button>Enabled (but does nothing)</button>
<button disabled>Disabled</button> <button disabled>Disabled</button>
@ -124,9 +161,10 @@ button</button>
<button bind-disabled="isUnchanged" on-click="onSave($event)">Disabled Cancel</button> <button bind-disabled="isUnchanged" on-click="onSave($event)">Disabled Cancel</button>
<button [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button> <button [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button>
<a class="to-toc" href="#toc">top</a>
<!-- property binding --> <!-- property binding -->
<hr><h2>Property Binding</h2> <hr><h2 id="property-binding">Property Binding</h2>
<!-- #docregion property-binding-1 --> <!-- #docregion property-binding-1 -->
<img [src]="heroImageUrl"> <img [src]="heroImageUrl">
@ -159,8 +197,10 @@ button</button>
<div [textContent]="'The title is ' + title"></div> <div [textContent]="'The title is ' + title"></div>
<!-- #enddocregion property-binding-vs-interpolation --> <!-- #enddocregion property-binding-vs-interpolation -->
<a class="to-toc" href="#toc">top</a>
<!-- attribute binding --> <!-- attribute binding -->
<hr><h2>Attribute Binding</h2> <hr><h2 id="attribute-binding">Attribute Binding</h2>
<!-- create and set a colspan attribute --> <!-- create and set a colspan attribute -->
<!-- #docregion attrib-binding-colspan --> <!-- #docregion attrib-binding-colspan -->
@ -197,12 +237,10 @@ button</button>
<button disabled [disabled]="false">Enabled (but inert)</button> <button disabled [disabled]="false">Enabled (but inert)</button>
</div> </div>
<a class="to-toc" href="#toc">top</a>
<!-- class binding --> <!-- class binding -->
<hr><h2>Class Binding</h2> <hr><h2 id="class-binding">Class Binding</h2>
<!-- #docregion class-binding-1 --> <!-- #docregion class-binding-1 -->
<!-- standard class attribute setting --> <!-- standard class attribute setting -->
@ -229,10 +267,10 @@ button</button>
<div bind-class.special="isSpecial">This class binding is special too</div> <div bind-class.special="isSpecial">This class binding is special too</div>
<a class="to-toc" href="#toc">top</a>
<!--style binding --> <!--style binding -->
<hr><h2>Style Binding</h2> <hr><h2 id="style-binding">Style Binding</h2>
<!-- #docregion style-binding-1 --> <!-- #docregion style-binding-1 -->
<button [style.color] = "isSpecial ? 'red': 'green'">Red</button> <button [style.color] = "isSpecial ? 'red': 'green'">Red</button>
@ -244,8 +282,10 @@ button</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button> <button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
<!-- #enddocregion style-binding-2 --> <!-- #enddocregion style-binding-2 -->
<a class="to-toc" href="#toc">top</a>
<!-- event binding --> <!-- event binding -->
<hr><h2>Event Binding</h2> <hr><h2 id="event-binding">Event Binding</h2>
<!-- #docregion event-binding-1 --> <!-- #docregion event-binding-1 -->
<button (click)="onSave()">Save</button> <button (click)="onSave()">Save</button>
@ -260,7 +300,7 @@ button</button>
<!-- `myClick` is an event on the custom `MyClickDirective` --> <!-- `myClick` is an event on the custom `MyClickDirective` -->
<!-- #docregion my-click --> <!-- #docregion my-click -->
<div myClick (myClick)="clickMessage=$event">click with myClick</div> <div (myClick)="clickMessage=$event">click with myClick</div>
<!-- #enddocregion my-click --> <!-- #enddocregion my-click -->
<!-- #enddocregion event-binding-3 --> <!-- #enddocregion event-binding-3 -->
{{clickMessage}} {{clickMessage}}
@ -301,10 +341,11 @@ button</button>
<!-- #enddocregion event-binding-propagation --> <!-- #enddocregion event-binding-propagation -->
<br><br> <br><br>
<a class="to-toc" href="#toc">top</a>
<!-- Two way data binding unwound; <!-- Two way data binding unwound;
passing the changed display value to the event handler via `$event` --> 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> <h3>Result: {{currentHero.firstName}}</h3>
@ -339,10 +380,10 @@ bindon-ngModel
(ngModelChange) = "setUpperCaseFirstName($event)" (ngModelChange) = "setUpperCaseFirstName($event)"
<br> <br>
<a class="to-toc" href="#toc">top</a>
<!-- NgClass binding --> <!-- NgClass binding -->
<hr><h2>NgClass Binding</h2> <hr><h2 id="ngClass">NgClass Binding</h2>
<p>setClasses returns {{setClasses() | json}}</p> <p>setClasses returns {{setClasses() | json}}</p>
<!-- #docregion NgClass-1 --> <!-- #docregion NgClass-1 -->
@ -359,6 +400,7 @@ bindon-ngModel
<div class="bad curly special">Bad curly special</div> <div class="bad curly special">Bad curly special</div>
<div [ngClass]="{bad:false, curly:true, special:true}">Curly special</div> <div [ngClass]="{bad:false, curly:true, special:true}">Curly special</div>
<a class="to-toc" href="#toc">top</a>
<!-- NgStyle binding --> <!-- NgStyle binding -->
<hr><h2>NgStyle Binding</h2> <hr><h2>NgStyle Binding</h2>
@ -384,10 +426,10 @@ bindon-ngModel
<!-- not used in chapter --> <!-- not used in chapter -->
<a class="to-toc" href="#toc">top</a>
<!-- NgIf binding --> <!-- NgIf binding -->
<hr><h2>NgIf Binding</h2> <hr><h2 id="ngIf">NgIf Binding</h2>
<!-- #docregion NgIf-1 --> <!-- #docregion NgIf-1 -->
<div *ngIf="currentHero != null">Hello, {{currentHero.firstName}}</div> <div *ngIf="currentHero != null">Hello, {{currentHero.firstName}}</div>
@ -402,7 +444,6 @@ bindon-ngModel
<hero-detail *ngIf="isActive"></hero-detail> <hero-detail *ngIf="isActive"></hero-detail>
<!-- #enddocregion NgIf-2 --> <!-- #enddocregion NgIf-2 -->
<!-- NgIf binding with template (no *) --> <!-- NgIf binding with template (no *) -->
<template [ngIf]="currentHero != null">Add {{currentHero.firstName}} with template</template> <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> <div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
<!-- #enddocregion NgIf-3 --> <!-- #enddocregion NgIf-3 -->
<a class="to-toc" href="#toc">top</a>
<!-- NgSwitch binding --> <!-- NgSwitch binding -->
<hr><h2>NgSwitch Binding</h2> <hr><h2 id="ngSwitch">NgSwitch Binding</h2>
<fieldset #toePicker (click)="toeChooser(toePicker)" > <fieldset #toePicker (click)="toeChooser(toePicker)" >
<input type="radio" name="toes" value="Eenie">Eenie <input type="radio" name="toes" value="Eenie">Eenie
@ -439,23 +480,39 @@ bindon-ngModel
</fieldset> </fieldset>
<div class="toe"> <div class="toe">
<div *ngIf="toeChoice == null">Pick a toe</div> <div *ngIf="toeChoice == null">Pick a toe</div>
<div *ngIf="toeChoice != null">You picked <div *ngIf="toeChoice != null">
<!-- #docregion NgSwitch --> You picked ...
<span [ngSwitch]="toeChoice"> <!-- #docregion NgSwitch, NgSwitch-expanded -->
<template [ngSwitchWhen]="'Eenie'">Eenie</template> <span [ngSwitch]="toeChoice">
<template [ngSwitchWhen]="'Meanie'">Meanie</template> <!-- #enddocregion NgSwitch -->
<template [ngSwitchWhen]="'Miney'">Miney</template>
<template [ngSwitchWhen]="'Moe'">Moe</template>
<template ngSwitchDefault>Other</template>
</span>
<!-- #enddocregion NgSwitch -->
</div>
<!-- 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> </div>
<a class="to-toc" href="#toc">top</a>
<!-- NgFor binding --> <!-- NgFor binding -->
<hr><h2>NgFor Binding</h2> <hr><h2 id="ngFor">NgFor Binding</h2>
<div class="box"> <div class="box">
<!-- #docregion NgFor-1 --> <!-- #docregion NgFor-1 -->
@ -470,52 +527,126 @@ bindon-ngModel
<hero-detail *ngFor="#hero of heroes" [hero]="hero"></hero-detail> <hero-detail *ngFor="#hero of heroes" [hero]="hero"></hero-detail>
<!-- #enddocregion NgFor-2 --> <!-- #enddocregion NgFor-2 -->
</div> </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"> <div class="box">
<!-- Ex: 1 - Hercules Son of Zeus -->
<!-- #docregion NgFor-3 --> <!-- #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 --> <!-- #enddocregion NgFor-3 -->
</div> </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 --> <!-- * 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 --> <!-- #docregion Template-1 -->
<hero-detail *ngIf="currentHero != null" [hero]="currentHero"></hero-detail> <hero-detail *ngIf="currentHero != null" [hero]="currentHero"></hero-detail>
<!-- #enddocregion Template-1 --> <!-- #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 --> <!-- #docregion Template-2 -->
<template [ngIf]="currentHero != null"> <template [ngIf]="currentHero != null">
<hero-detail [hero]="currentHero"></hero-detail> <hero-detail [hero]="currentHero"></hero-detail>
</template> </template>
<!-- #enddocregion Template-2 --> <!-- #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"> <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 --> <!-- #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 --> <!-- #enddocregion Template-3 -->
</div> </div>
<br> <br>
<p><i>expand to &lt;template&gt;</i></p>
<div class="box"> <div class="box">
<!-- ngFor w/ hero-detail Component inside a template element --> <!-- ngFor w/ hero-detail Component inside a template element -->
<!-- #docregion Template-4 --> <!-- #docregion Template-4 -->
<template ngFor #hero [ngForOf]="heroes"> <template ngFor #hero [ngForOf]="heroes" [ngForTrackBy]="trackByHeroes>
<hero-detail [hero]="hero"></hero-detail> <hero-detail [hero]="hero"></hero-detail>
</template> </template>
<!-- #enddocregion Template-4 --> <!-- #enddocregion Template-4 -->
</div> </div>
<a class="to-toc" href="#toc">top</a>
<!-- template local variable --> <!-- template local variable -->
<hr><h2>Template local variables</h2> <hr><h2 id="local-vars">Template local variables</h2>
<!-- #docregion var-phone --> <!-- #docregion var-phone -->
<!-- phone refers to the input element; pass its `value` to an event handler --> <!-- 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 --> <!-- btn refers to the button element; show its disabled state -->
<button #btn disabled [textContent]="'disabled by attribute: ' + btn.disabled.toString()"></button> <button #btn disabled [textContent]="'disabled by attribute: ' + btn.disabled.toString()"></button>
<a class="to-toc" href="#toc">top</a>
<!-- inputs and output --> <!-- inputs and output -->
<hr><h2>Inputs and Outputs</h2> <hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2>
<!-- #docregion io-1 --> <!-- #docregion io-1 -->
<img [src]="iconUrl"/> <img [src]="iconUrl"/>
@ -560,11 +693,13 @@ bindon-ngModel
</hero-detail> </hero-detail>
<!-- #enddocregion io-2 --> <!-- #enddocregion io-2 -->
<div myClick2 (myClick)="clickMessage2=$event">myClick2</div> <div (myClick)="clickMessage2=$event">myClick2</div>
{{clickMessage2}} {{clickMessage2}}
<a class="to-toc" href="#toc">top</a>
<!-- Pipes --> <!-- Pipes -->
<hr><h2>Pipes</h2> <hr><h2 id="pipes">Pipes</h2>
<!-- #docregion pipes-1 --> <!-- #docregion pipes-1 -->
<!-- Force title to uppercase --> <!-- Force title to uppercase -->
@ -596,9 +731,10 @@ bindon-ngModel
<label>Price: </label>{{product['price'] | currency:'$'}} <label>Price: </label>{{product['price'] | currency:'$'}}
</div> </div>
<a class="to-toc" href="#toc">top</a>
<!-- Null values and the Elvis operator --> <!-- Null values and the Elvis operator -->
<hr><h2>Elvis</h2> <hr><h2 id="elvis">Elvis <i>?.</i></h2>
<div> <div>
<!-- #docregion elvis-1 --> <!-- #docregion elvis-1 -->
@ -645,8 +781,16 @@ The null hero's name is {{nullHero.firstName}}
<!-- Todo: discuss this in the Style binding section --> <!-- Todo: discuss this in the Style binding section -->
<!-- enums in bindings --> <!-- 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>The current color number is {{color}}</p>
<p><button [style.color]="color.toString()" (click)="colorToggle()">Enum Toggle</button> <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;} .bad {color: red;}
.curly {font-family: "Brush Script MT"} .curly {font-family: "Brush Script MT"}
.toe {margin-left: 1em; font-style: italic;} .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 --> <!-- #docplaster -->
<h3>My First Angular Application</h3> <a id="toc"></a>
<!-- #enddocregion my-first-app --> <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 --> <!-- Interpolation and expressions -->
<hr><h2>Interpolation</h2> <hr><h2 id="interpolation">Interpolation</h2>
<!-- #docregion first-interpolation --> <!-- #docregion first-interpolation -->
<p>My current hero is {{currentHero.firstName}}</p> <p>My current hero is {{currentHero.firstName}}</p>
@ -26,9 +61,10 @@
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p> <p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
<!-- #enddocregion sum-2 --> <!-- #enddocregion sum-2 -->
<a class="to-toc" href="#toc">top</a>
<!-- New Mental Model --> <!-- 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">--> <!--<img src="http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png">-->
<!-- Public Domain terms of use: http://www.wpclipart.com/terms.html --> <!-- Public Domain terms of use: http://www.wpclipart.com/terms.html -->
@ -65,11 +101,10 @@
</div> </div>
<br><br> <br><br>
<!-- See https://github.com/angular/angular/issues/5707 about "myClick (myClick)" -->
<!-- #docregion event-binding-syntax-1 --> <!-- #docregion event-binding-syntax-1 -->
<button (click) = "onSave()">Save</button> <button (click) = "onSave()">Save</button>
<hero-detail (deleted)="onHeroDeleted()"></hero-detail> <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 --> <!-- #enddocregion event-binding-syntax-1 -->
{{clicked}} {{clicked}}
<br><br> <br><br>
@ -97,8 +132,10 @@
<!-- #enddocregion style-binding-syntax-1 --> <!-- #enddocregion style-binding-syntax-1 -->
button</button> button</button>
<a class="to-toc" href="#toc">top</a>
<!-- property vs. attribute --> <!-- 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 --> <!-- examine the following <img> tag in the browser tools -->
<img src="images/ng-logo.png" <img src="images/ng-logo.png"
[src]="heroImageUrl"> [src]="heroImageUrl">
@ -109,10 +146,10 @@ button</button>
<img bind-src="heroImageUrl"/> <img bind-src="heroImageUrl"/>
<img [attr.src]="villainImageUrl"/> <img [attr.src]="villainImageUrl"/>
<a class="to-toc" href="#toc">top</a>
<!-- buttons --> <!-- buttons -->
<hr><h2>Buttons</h2> <hr><h2 id="buttons">Buttons</h2>
<button>Enabled (but does nothing)</button> <button>Enabled (but does nothing)</button>
<button disabled>Disabled</button> <button disabled>Disabled</button>
@ -124,9 +161,10 @@ button</button>
<button bind-disabled="isUnchanged" on-click="onSave($event)">Disabled Cancel</button> <button bind-disabled="isUnchanged" on-click="onSave($event)">Disabled Cancel</button>
<button [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button> <button [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button>
<a class="to-toc" href="#toc">top</a>
<!-- property binding --> <!-- property binding -->
<hr><h2>Property Binding</h2> <hr><h2 id="property-binding">Property Binding</h2>
<!-- #docregion property-binding-1 --> <!-- #docregion property-binding-1 -->
<img [src]="heroImageUrl"> <img [src]="heroImageUrl">
@ -157,8 +195,10 @@ button</button>
<div [textContent]="'The title is '+title"></div> <div [textContent]="'The title is '+title"></div>
<!-- #enddocregion property-binding-vs-interpolation --> <!-- #enddocregion property-binding-vs-interpolation -->
<a class="to-toc" href="#toc">top</a>
<!-- attribute binding --> <!-- attribute binding -->
<hr><h2>Attribute Binding</h2> <hr><h2 id="attribute-binding">Attribute Binding</h2>
<!-- create and set a colspan attribute --> <!-- create and set a colspan attribute -->
<!-- #docregion attrib-binding-colspan --> <!-- #docregion attrib-binding-colspan -->
@ -195,12 +235,10 @@ button</button>
<button disabled [disabled]="false">Enabled (but inert)</button> <button disabled [disabled]="false">Enabled (but inert)</button>
</div> </div>
<a class="to-toc" href="#toc">top</a>
<!-- class binding --> <!-- class binding -->
<hr><h2>Class Binding</h2> <hr><h2 id="class-binding">Class Binding</h2>
<!-- #docregion class-binding-1 --> <!-- #docregion class-binding-1 -->
<!-- standard class attribute setting --> <!-- standard class attribute setting -->
@ -226,10 +264,10 @@ button</button>
<div bind-class.special="isSpecial">This class binding is special too</div> <div bind-class.special="isSpecial">This class binding is special too</div>
<a class="to-toc" href="#toc">top</a>
<!--style binding --> <!--style binding -->
<hr><h2>Style Binding</h2> <hr><h2 id="style-binding">Style Binding</h2>
<!-- #docregion style-binding-1 --> <!-- #docregion style-binding-1 -->
<button [style.color] = "isSpecial ? 'red' : 'green'">Red</button> <button [style.color] = "isSpecial ? 'red' : 'green'">Red</button>
@ -241,8 +279,10 @@ button</button>
<button [style.fontSize.%]="!isSpecial ? 150 : 50" >Small</button> <button [style.fontSize.%]="!isSpecial ? 150 : 50" >Small</button>
<!-- #enddocregion style-binding-2 --> <!-- #enddocregion style-binding-2 -->
<a class="to-toc" href="#toc">top</a>
<!-- event binding --> <!-- event binding -->
<hr><h2>Event Binding</h2> <hr><h2 id="event-binding">Event Binding</h2>
<!-- #docregion event-binding-1 --> <!-- #docregion event-binding-1 -->
<button (click)="onSave()">Save</button> <button (click)="onSave()">Save</button>
@ -257,7 +297,7 @@ button</button>
<!-- `myClick` is an event on the custom `MyClickDirective` --> <!-- `myClick` is an event on the custom `MyClickDirective` -->
<!-- #docregion my-click --> <!-- #docregion my-click -->
<div myClick (myClick)="clickMessage=$event">click with myClick</div> <div (myClick)="clickMessage=$event">click with myClick</div>
<!-- #enddocregion my-click --> <!-- #enddocregion my-click -->
<!-- #enddocregion event-binding-3 --> <!-- #enddocregion event-binding-3 -->
{{clickMessage}} {{clickMessage}}
@ -298,10 +338,11 @@ button</button>
<!-- #enddocregion event-binding-propagation --> <!-- #enddocregion event-binding-propagation -->
<br><br> <br><br>
<a class="to-toc" href="#toc">top</a>
<!-- Two way data binding unwound; <!-- Two way data binding unwound;
passing the changed display value to the event handler via `$event` --> 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> <h3>Result: {{currentHero.firstName}}</h3>
@ -336,10 +377,10 @@ bindon-ngModel
(ngModelChange) = "setUpperCaseFirstName($event)" (ngModelChange) = "setUpperCaseFirstName($event)"
<br> <br>
<a class="to-toc" href="#toc">top</a>
<!-- NgClass binding --> <!-- NgClass binding -->
<hr><h2>NgClass Binding</h2> <hr><h2 id="ngClass">NgClass Binding</h2>
<p>setClasses returns {{setClasses() | json}}</p> <p>setClasses returns {{setClasses() | json}}</p>
<!-- #docregion NgClass-1 --> <!-- #docregion NgClass-1 -->
@ -356,9 +397,10 @@ After setClasses(), the classes are "{{classDiv.className}}"
<div class="bad curly special">Bad curly special</div> <div class="bad curly special">Bad curly special</div>
<div [ngClass]="{bad:false, curly:true, special:true}">Curly special</div> <div [ngClass]="{bad:false, curly:true, special:true}">Curly special</div>
<a class="to-toc" href="#toc">top</a>
<!-- NgStyle binding --> <!-- NgStyle binding -->
<hr><h2>NgStyle Binding</h2> <hr><h2 id="ngStyle">NgStyle Binding</h2>
<!-- #docregion NgStyle-1 --> <!-- #docregion NgStyle-1 -->
<div [style.fontSize]="isSpecial ? 'x-large' : 'smaller'" > <div [style.fontSize]="isSpecial ? 'x-large' : 'smaller'" >
@ -381,10 +423,10 @@ After setClasses(), the classes are "{{classDiv.className}}"
<!-- not used in chapter --> <!-- not used in chapter -->
<a class="to-toc" href="#toc">top</a>
<!-- NgIf binding --> <!-- NgIf binding -->
<hr><h2>NgIf Binding</h2> <hr><h2 id="ngIf">NgIf Binding</h2>
<!-- #docregion NgIf-1 --> <!-- #docregion NgIf-1 -->
<div *ngIf="currentHero">Hello, {{currentHero.firstName}}</div> <div *ngIf="currentHero">Hello, {{currentHero.firstName}}</div>
@ -399,7 +441,6 @@ After setClasses(), the classes are "{{classDiv.className}}"
<hero-detail *ngIf="isActive"></hero-detail> <hero-detail *ngIf="isActive"></hero-detail>
<!-- #enddocregion NgIf-2 --> <!-- #enddocregion NgIf-2 -->
<!-- NgIf binding with template (no *) --> <!-- NgIf binding with template (no *) -->
<template [ngIf]="currentHero">Add {{currentHero.firstName}} with template</template> <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> <div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
<!-- #enddocregion NgIf-3 --> <!-- #enddocregion NgIf-3 -->
<a class="to-toc" href="#toc">top</a>
<!-- NgSwitch binding --> <!-- NgSwitch binding -->
<hr><h2>NgSwitch Binding</h2> <hr><h2 id="ngSwitch">NgSwitch Binding</h2>
<fieldset #toePicker (click)="toeChooser(toePicker)" > <fieldset #toePicker (click)="toeChooser(toePicker)" >
<input type="radio" name="toes" value="Eenie">Eenie <input type="radio" name="toes" value="Eenie">Eenie
@ -437,22 +478,38 @@ After setClasses(), the classes are "{{classDiv.className}}"
<div class="toe"> <div class="toe">
<div *ngIf="!toeChoice">Pick a toe</div> <div *ngIf="!toeChoice">Pick a toe</div>
<div *ngIf="toeChoice">You picked <div *ngIf="toeChoice">
<!-- #docregion NgSwitch --> You picked ...
<!-- #docregion NgSwitch, NgSwitch-expanded -->
<span [ngSwitch]="toeChoice"> <span [ngSwitch]="toeChoice">
<template [ngSwitchWhen]="'Eenie'">Eenie</template> <!-- #enddocregion NgSwitch -->
<template [ngSwitchWhen]="'Meanie'">Meanie</template>
<template [ngSwitchWhen]="'Miney'">Miney</template>
<template [ngSwitchWhen]="'Moe'">Moe</template>
<template ngSwitchDefault>Other</template>
</span>
<!-- #enddocregion NgSwitch -->
</div>
<!-- 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> </div>
<a class="to-toc" href="#toc">top</a>
<!-- NgFor binding --> <!-- NgFor binding -->
<hr><h2>NgFor Binding</h2> <hr><h2 id="ngFor">NgFor Binding</h2>
<div class="box"> <div class="box">
<!-- #docregion NgFor-1 --> <!-- #docregion NgFor-1 -->
@ -467,52 +524,125 @@ After setClasses(), the classes are "{{classDiv.className}}"
<hero-detail *ngFor="#hero of heroes" [hero]="hero"></hero-detail> <hero-detail *ngFor="#hero of heroes" [hero]="hero"></hero-detail>
<!-- #enddocregion NgFor-2 --> <!-- #enddocregion NgFor-2 -->
</div> </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"> <div class="box">
<!-- Ex: 1 - Hercules Son of Zeus -->
<!-- #docregion NgFor-3 --> <!-- #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 --> <!-- #enddocregion NgFor-3 -->
</div> </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 --> <!-- * 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 --> <!-- #docregion Template-1 -->
<hero-detail *ngIf="currentHero" [hero]="currentHero"></hero-detail> <hero-detail *ngIf="currentHero" [hero]="currentHero"></hero-detail>
<!-- #enddocregion Template-1 --> <!-- #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 --> <!-- #docregion Template-2 -->
<template [ngIf]="currentHero"> <template [ngIf]="currentHero">
<hero-detail [hero]="currentHero"></hero-detail> <hero-detail [hero]="currentHero"></hero-detail>
</template> </template>
<!-- #enddocregion Template-2 --> <!-- #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"> <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 --> <!-- #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 --> <!-- #enddocregion Template-3 -->
</div> </div>
<br>
<p><i>expand to &lt;template&gt;</i></p>
<div class="box"> <div class="box">
<!-- ngFor w/ hero-detail Component inside a template element --> <!-- ngFor w/ hero-detail Component inside a template element -->
<!-- #docregion Template-4 --> <!-- #docregion Template-4 -->
<template ngFor #hero [ngForOf]="heroes"> <template ngFor #hero [ngForOf]="heroes" [ngForTrackBy]="trackByHeroes">
<hero-detail [hero]="hero"></hero-detail> <hero-detail [hero]="hero"></hero-detail>
</template> </template>
<!-- #enddocregion Template-4 --> <!-- #enddocregion Template-4 -->
</div> </div>
<a class="to-toc" href="#toc">top</a>
<!-- template local variable --> <!-- template local variable -->
<hr><h2>Template local variables</h2> <hr><h2 id="local-vars">Template local variables</h2>
<!-- #docregion var-phone --> <!-- #docregion var-phone -->
<!-- phone refers to the input element; pass its `value` to an event handler --> <!-- 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 --> <!-- btn refers to the button element; show its disabled state -->
<button #btn disabled [textContent]="'disabled by attribute: '+btn.disabled"></button> <button #btn disabled [textContent]="'disabled by attribute: '+btn.disabled"></button>
<a class="to-toc" href="#toc">top</a>
<!-- inputs and output --> <!-- inputs and output -->
<hr><h2>Inputs and Outputs</h2> <hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2>
<!-- #docregion io-1 --> <!-- #docregion io-1 -->
<img [src]="iconUrl"/> <img [src]="iconUrl"/>
@ -557,11 +689,13 @@ After setClasses(), the classes are "{{classDiv.className}}"
</hero-detail> </hero-detail>
<!-- #enddocregion io-2 --> <!-- #enddocregion io-2 -->
<div myClick2 (myClick)="clickMessage2=$event">myClick2</div> <div (myClick)="clickMessage2=$event">myClick2</div>
{{clickMessage2}} {{clickMessage2}}
<a class="to-toc" href="#toc">top</a>
<!-- Pipes --> <!-- Pipes -->
<hr><h2>Pipes</h2> <hr><h2 id="pipes">Pipes</h2>
<!-- #docregion pipes-1 --> <!-- #docregion pipes-1 -->
<!-- Force title to uppercase --> <!-- Force title to uppercase -->
@ -597,9 +731,10 @@ After setClasses(), the classes are "{{classDiv.className}}"
<label>Price: </label>{{product.price | currency:'USD':true}} <label>Price: </label>{{product.price | currency:'USD':true}}
</div> </div>
<a class="to-toc" href="#toc">top</a>
<!-- Null values and the Elvis operator --> <!-- Null values and the Elvis operator -->
<hr><h2>Elvis</h2> <hr><h2 id="elvis">Elvis <i>?.</i></h2>
<div> <div>
<!-- #docregion elvis-1 --> <!-- #docregion elvis-1 -->
@ -646,10 +781,20 @@ The null hero's name is {{nullHero?.firstName}}
</div> </div>
<a class="to-toc" href="#toc">top</a>
<!-- Todo: discuss this in the Style binding section --> <!-- Todo: discuss this in the Style binding section -->
<!-- enums in bindings --> <!-- 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 name of the Color.Red enum is {{Color[Color.Red]}}</p>
<p>The current color number is {{color}}</p> <p>The current color number is {{color}}</p>
<p><button [style.color]="Color[color]" (click)="colorToggle()">Enum Toggle</button> <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 //#docplaster
import {Component} from 'angular2/core'; import {Component, AfterViewInit, ElementRef, OnInit, QueryList, ViewChildren} from 'angular2/core';
import {NgForm} from 'angular2/common'; import {NgForm} from 'angular2/common';
import {Hero} from './hero'; import {Hero} from './hero';
@ -25,7 +25,15 @@ export enum Color {Red, Green, Blue};
MyClickDirective, MyClickDirective2 MyClickDirective, MyClickDirective2
] ]
}) })
export class AppComponent { export class AppComponent implements AfterViewInit, OnInit {
ngOnInit(){
this.refreshHeroes();
}
ngAfterViewInit() {
this._detectNgForTrackByEffects();
}
actionName = 'Go for it'; actionName = 'Go for it';
alert = alerter; alert = alerter;
@ -56,7 +64,7 @@ export class AppComponent {
getVal() {return this.val}; getVal() {return this.val};
heroes = Hero.MockHeroes; heroes:Hero[];
// heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png'; // heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
// Public Domain terms of use: http://www.wpclipart.com/terms.html // Public Domain terms of use: http://www.wpclipart.com/terms.html
@ -101,6 +109,26 @@ export class AppComponent {
price: 42 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){ setUpperCaseFirstName(firstName:string){
//console.log(firstName); //console.log(firstName);
this.currentHero.firstName = firstName.toUpperCase(); 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; val=2;
// villainImageUrl = 'http://www.clker.com/cliparts/u/s/y/L/x/9/villain-man-hi.png' // 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 // Public Domain terms of use http://www.clker.com/disclaimer.html
villainImageUrl = 'images/villain.png' 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 lastName?:string,
public birthdate?:Date, public birthdate?:Date,
public url?:string, public url?:string,
public rate:number = 100) { public rate:number = 100,
this.id = Hero.nextId++; 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}`;} get fullName() {return `${this.firstName} ${this.lastName}`;}
static nextId = 1; static nextId = 1;

View File

@ -8,4 +8,5 @@ img {height: 100px;}
.bad {color: red;} .bad {color: red;}
.curly {font-family: "Brush Script MT"} .curly {font-family: "Brush Script MT"}
.toe {margin-left: 1em; font-style: italic;} .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') +includeShared('../../../ts/latest/guide/template-syntax.jade', 'directives-ngFor-6')
+makeExample('template-syntax/dart/lib/app_component.html', 'NgFor-3')(format=".") +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-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')
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-1') +includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-1')
+makeExample('template-syntax/dart/lib/app_component.html', 'Template-1')(format=".") +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') +includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-2')
+makeExample('template-syntax/dart/lib/app_component.html', 'Template-2')(format=".") +makeExample('template-syntax/dart/lib/app_component.html', 'Template-2')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-3') +includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngIf-3')
@ -299,8 +306,11 @@ table
Dont make the mistake of writing `ngIf="currentHero"`! Dont make the mistake of writing `ngIf="currentHero"`!
That syntax assigns the *string* value "currentHero" to `ngIf`, That syntax assigns the *string* value "currentHero" to `ngIf`,
which won't work because `ngIf` expects a bool. **[QUESTION: Did I get that right?]** 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') +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') +includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngFor-2')
+makeExample('template-syntax/dart/lib/app_component.html', 'Template-3')(format=".") +makeExample('template-syntax/dart/lib/app_component.html', 'Template-3')(format=".")
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'star-template-ngFor-3') +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) * [Event binding](#event-binding)
* [Two-way data binding with `NgModel`](#ngModel) * [Two-way data binding with `NgModel`](#ngModel)
* [Built-in directives](#directives) * [Built-in directives](#directives)
* [NgClass](#ngClass)
* [NgStyle](#ngStyle)
* [NgIf](#ngIf)
* [NgSwitch](#ngSwitch)
* [NgFor](#ngFor)
* [* and &lt;template>](#star-template) * [* and &lt;template>](#star-template)
* [Local template variables](#local-vars) * [Local template variables](#local-vars)
* [Input and output properties](#inputs-outputs) * [Input and output properties](#inputs-outputs)
@ -1078,18 +1083,35 @@ figure.image-display
+makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch')(format=".")
// #docregion directives-ngSwitch-2 // #docregion directives-ngSwitch-2
:marked :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.** **At any particular moment, at most one of these *spans* 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.
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: Three collaborating directives are at work here:
1. `ngSwitch`: bound to an expression that returns the switch value 1. `ngSwitch`: bound to an expression that returns the switch value
1. `ngSwitchWhen`: bound to an expression returning a match 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 // #enddocregion directives-ngSwitch-2
// #docregion directives-ngFor-1 // #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". 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 // #enddocregion directives-ngFor-6
+makeExample('template-syntax/ts/app/app.component.html', 'NgFor-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgFor-3')(format=".")
// #docregion directives-ngFor-7 // #docregion directives-ngFor-7
.l-sub-section .l-sub-section
:marked :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 // #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 // #docregion star-template
<a name="star-template"></a> <a name="star-template"></a>
<a name="structural-directive"></a> <a name="structural-directive"></a>
.l-main-section .l-main-section
:marked :marked
## * and &lt;template> ## * and &lt;template&gt;
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. 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 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. with the help of templates.
`NgFor`, `NgIf`, and `NgSwitch` all add and remove element subtrees that are wrapped in `<template>` tags. `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. We didn't see the `<template>` tags because the `*` prefix syntax allowed us to skip those tags and
There isnt much choice; we define a different template for each switch choice focus directly on the HTML element that we are including, excluding, or repeating.
and let the directive render the template that matches the switch value.
In this section we go under the hood and see how
[NgFor](#ngFor) and [NgIf](#ngIf), on the other hand, each need only one template: Angular strips away the `*` and expands the HTML into the `<template>` tags for us.
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.
// #enddocregion star-template // #enddocregion star-template
// #docregion star-template-ngIf-1 // #docregion star-template-ngIf-1
:marked :marked
### Expanding `*ngIf` ### 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 // #enddocregion star-template-ngIf-1
+makeExample('template-syntax/ts/app/app.component.html', 'Template-1')(format=".") +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 // #docregion star-template-ngIf-2
:marked :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 // #enddocregion star-template-ngIf-2
+makeExample('template-syntax/ts/app/app.component.html', 'Template-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'Template-2')(format=".")
// #docregion star-template-ngIf-3 // #docregion star-template-ngIf-3
:marked :marked
Notice that the `*` is gone and we have a [property binding](#property-binding) to the `ngIf` Notice that the `[hero]="currentHero"` binding remains on the child `<hero-detail>`
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>`
element inside the template. element inside the template.
// #enddocregion star-template-ngIf-3 // #enddocregion star-template-ngIf-3
@ -1219,26 +1275,48 @@ figure.image-display
… even when there is no `currentHero`! … even when there is no `currentHero`!
// #enddocregion star-template-ngIf-4 // #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 // #docregion star-template-ngFor-1
:marked :marked
### Expanding `*ngFor` ### 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 // #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 // #docregion star-template-ngFor-2
:marked :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 // #enddocregion star-template-ngFor-2
+makeExample('template-syntax/ts/app/app.component.html', 'Template-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'Template-3')(format=".")
// #docregion star-template-ngFor-3 // #docregion star-template-ngFor-3
:marked :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 // #enddocregion star-template-ngFor-3
+makeExample('template-syntax/ts/app/app.component.html', 'Template-4')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'Template-4')(format=".")
// #docregion star-template-ngFor-4 // #docregion star-template-ngFor-4
:marked :marked
The `NgFor` code is a bit more complex than `NgIf` because a repeater has more moving parts to configure. 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. Using the `*ngFor` syntax is much easier than writing out this expanded HTML ourselves.
// #enddocregion star-template-ngFor-4 // #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