docs(aio): update migrated content from anguar.io

This commit is contained in:
Peter Bacon Darwin
2017-03-27 16:08:53 +01:00
committed by Pete Bacon Darwin
parent ff82756415
commit fd72fad8fd
1901 changed files with 20145 additions and 45127 deletions

View File

@ -0,0 +1,17 @@
a.to-toc { margin: 30px 0; }
button { font-size: 100%; margin: 0 2px; }
div[clickable] {cursor: pointer; max-width: 200px; margin: 16px 0}
#noTrackByCnt, #withTrackByCnt {color: darkred; max-width: 450px; margin: 4px;}
img {height: 100px;}
.box {border: 1px solid black; padding: 6px; max-width: 450px;}
.child-div {margin-left: 1em; font-weight: normal}
.context {margin-left: 1em;}
.hidden {display: none}
.parent-div {margin-top: 1em; font-weight: bold}
.special {font-weight:bold; font-size: x-large}
.bad {color: red;}
.saveable {color: limegreen;}
.curly, .modified {font-family: "Brush Script MT"}
.toe {margin-left: 1em; font-style: italic;}
little-hero {color:blue; font-size: smaller; background-color: Turquoise }
.to-toc {margin-top: 10px; display: block}

View File

@ -0,0 +1,820 @@
<!-- #docplaster -->
<a id="toc"></a>
<h1>Template Syntax</h1>
<a href="#interpolation">Interpolation</a><br>
<a href="#expression-context">Expression context</a><br>
<a href="#statement-context">Statement context</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>
<a href="#two-way">Two-way 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="#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>
<a href="#ngSwitch">NgSwitch</a><br>
</div>
<br>
<a href="#ref-vars">Template reference variables</a><br>
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
<a href="#pipes">Pipes</a><br>
<a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br>
<a href="#enums">Enums</a><br>
<!-- Interpolation and expressions -->
<hr><h2 id="interpolation">Interpolation</h2>
<!-- #docregion first-interpolation -->
<p>My current hero is {{currentHero.name}}</p>
<!-- #enddocregion first-interpolation -->
<!-- #docregion title+image -->
<h3>
{{title}}
<img src="{{heroImageUrl}}" style="height:30px">
</h3>
<!-- #enddocregion title+image -->
<!-- #docregion sum-1 -->
<!-- "The sum of 1 + 1 is 2" -->
<p>The sum of 1 + 1 is {{1 + 1}}</p>
<!-- #enddocregion sum-1 -->
<!-- #docregion sum-2 -->
<!-- "The sum of 1 + 1 is not 4" -->
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
<!-- #enddocregion sum-2 -->
<a class="to-toc" href="#toc">top</a>
<hr><h2 id="expression-context">Expression context</h2>
<p>Component expression context (&#123;&#123;title&#125;&#125;, [hidden]="isUnchanged")</p>
<div class="context">
<!-- #docregion context-component-expression -->
{{title}}
<span [hidden]="isUnchanged">changed</span>
<!-- #enddocregion context-component-expression -->
</div>
<p>Template input variable expression context (let hero)</p>
<!-- template hides the following; plenty of examples later -->
<template>
<!-- #docregion context-var -->
<div *ngFor="let hero of heroes">{{hero.name}}</div>
<!-- #enddocregion context-var -->
</template>
<p>Template reference variable expression context (#heroInput)</p>
<div (keyup)="0" class="context">
Type something:
<!-- #docregion context-var -->
<input #heroInput> {{heroInput.value}}
<!-- #enddocregion context-var -->
</div>
<a class="to-toc" href="#toc">top</a>
<hr><h2 id="statement-context">Statement context</h2>
<p>Component statement context ( (click)="onSave() )
<div class="context">
<!-- #docregion context-component-statement -->
<button (click)="deleteHero()">Delete hero</button>
<!-- #enddocregion context-component-statement -->
</div>
<p>Template $event statement context</p>
<div class="context">
<!-- #docregion context-var-statement -->
<button (click)="onSave($event)">Save</button>
<!-- #enddocregion context-var-statement -->
</div>
<p>Template input variable statement context (let hero)</p>
<!-- template hides the following; plenty of examples later -->
<div class="context">
<!-- #docregion context-var-statement -->
<button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button>
<!-- #enddocregion context-var-statement -->
</div>
<p>Template reference variable statement context (#heroForm)</p>
<div class="context">
<!-- #docregion context-var-statement -->
<form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>
<!-- #enddocregion context-var-statement -->
</div>
<a class="to-toc" href="#toc">top</a>
<!-- New Mental Model -->
<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 -->
<!-- #docregion img+button -->
<div class="special">Mental Model</div>
<img src="images/hero.png">
<button disabled>Save</button>
<!-- #enddocregion img+button -->
<br><br>
<div>
<!-- #docregion hero-detail-1 -->
<!-- Normal HTML -->
<div class="special">Mental Model</div>
<!-- Wow! A new element! -->
<hero-detail></hero-detail>
<!-- #enddocregion hero-detail-1 -->
</div>
<br><br>
<div>
<!-- #docregion disabled-button-1 -->
<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Save</button>
<!-- #enddocregion disabled-button-1 -->
</div>
<br><br>
<div>
<!-- #docregion property-binding-syntax-1 -->
<img [src]="heroImageUrl">
<hero-detail [hero]="currentHero"></hero-detail>
<div [ngClass]="{special: isSpecial}"></div>
<!-- #enddocregion property-binding-syntax-1 -->
</div>
<br><br>
<!-- #docregion event-binding-syntax-1 -->
<button (click)="onSave()">Save</button>
<hero-detail (deleteRequest)="deleteHero()"></hero-detail>
<div (myClick)="clicked=$event" clickable>click me</div>
<!-- #enddocregion event-binding-syntax-1 -->
{{clicked}}
<br><br>
<div>
Hero Name:
<!-- #docregion 2-way-binding-syntax-1 -->
<input [(ngModel)]="name">
<!-- #enddocregion 2-way-binding-syntax-1 -->
{{name}}
</div>
<br><br>
<!-- #docregion attribute-binding-syntax-1 -->
<button [attr.aria-label]="help">help</button>
<!-- #enddocregion attribute-binding-syntax-1 -->
<br><br>
<!-- #docregion class-binding-syntax-1 -->
<div [class.special]="isSpecial">Special</div>
<!-- #enddocregion class-binding-syntax-1 -->
<br><br>
<!-- #docregion style-binding-syntax-1 -->
<button [style.color]="isSpecial ? 'red' : 'green'">
<!-- #enddocregion style-binding-syntax-1 -->
button</button>
<a class="to-toc" href="#toc">top</a>
<!-- property vs. attribute -->
<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">
<br><br>
<img [src]="iconUrl"/>
<img bind-src="heroImageUrl"/>
<img [attr.src]="villainImageUrl"/>
<a class="to-toc" href="#toc">top</a>
<!-- buttons -->
<hr><h2 id="buttons">Buttons</h2>
<button>Enabled (but does nothing)</button>
<button disabled>Disabled</button>
<button disabled=false>Still disabled</button>
<br><br>
<button disabled>disabled by attribute</button>
<button [disabled]="isUnchanged">disabled by property binding</button>
<br><br>
<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 id="property-binding">Property Binding</h2>
<!-- #docregion property-binding-1 -->
<img [src]="heroImageUrl">
<!-- #enddocregion property-binding-1 -->
<!-- #docregion property-binding-2 -->
<button [disabled]="isUnchanged">Cancel is disabled</button>
<!-- #enddocregion property-binding-2 -->
<!-- #docregion property-binding-3 -->
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
<!-- #enddocregion property-binding-3 -->
<!-- #docregion property-binding-4 -->
<hero-detail [hero]="currentHero"></hero-detail>
<!-- #enddocregion property-binding-4 -->
<!-- #docregion property-binding-5 -->
<img bind-src="heroImageUrl">
<!-- #enddocregion property-binding-5 -->
<!-- #docregion property-binding-6 -->
<!-- ERROR: HeroDetailComponent.hero expects a
Hero object, not the string "currentHero" -->
<!-- #enddocregion property-binding-6 -->
<div *ngIf="false">
<!-- #docregion property-binding-6 -->
<hero-detail hero="currentHero"></hero-detail>
<!-- #enddocregion property-binding-6 -->
</div>
<!-- #docregion property-binding-7 -->
<hero-detail prefix="You are my" [hero]="currentHero"></hero-detail>
<!-- #enddocregion property-binding-7 -->
<!-- #docregion property-binding-vs-interpolation -->
<p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p>
<p><img [src]="heroImageUrl"> is the <i>property bound</i> image.</p>
<p><span>"{{title}}" is the <i>interpolated</i> title.</span></p>
<p>"<span [innerHTML]="title"></span>" is the <i>property bound</i> title.</p>
<!-- #enddocregion property-binding-vs-interpolation -->
<!-- #docregion property-binding-vs-interpolation-sanitization -->
<!--
Angular generates warnings for these two lines as it sanitizes them
WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).
-->
<p><span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span></p>
<p>"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil title.</p>
<!-- #enddocregion property-binding-vs-interpolation-sanitization -->
<a class="to-toc" href="#toc">top</a>
<!-- attribute binding -->
<hr><h2 id="attribute-binding">Attribute Binding</h2>
<!-- create and set a colspan attribute -->
<!-- #docregion attrib-binding-colspan -->
<table border=1>
<!-- expression calculates colspan=2 -->
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
<!-- ERROR: There is no `colspan` property to set!
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
-->
<tr><td>Five</td><td>Six</td></tr>
</table>
<!-- #enddocregion attrib-binding-colspan -->
<br>
<!-- #docregion attrib-binding-aria -->
<!-- create and set an aria attribute for assistive technology -->
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
<!-- #enddocregion attrib-binding-aria -->
<br><br>
<!-- The following effects are not discussed in the chapter -->
<div>
<!-- any use of [attr.disabled] creates the disabled attribute -->
<button [attr.disabled]="isUnchanged">Disabled</button>
<button [attr.disabled]="!isUnchanged">Disabled as well</button>
<!-- we'd have to remove it with property binding -->
<button disabled [disabled]="false">Enabled (but inert)</button>
</div>
<a class="to-toc" href="#toc">top</a>
<!-- class binding -->
<hr><h2 id="class-binding">Class Binding</h2>
<!-- #docregion class-binding-1 -->
<!-- standard class attribute setting -->
<div class="bad curly special">Bad curly special</div>
<!-- #enddocregion class-binding-1 -->
<!-- #docregion class-binding-2 -->
<!-- reset/override all class names with a binding -->
<div class="bad curly special"
[class]="badCurly">Bad curly</div>
<!-- #enddocregion class-binding-2 -->
<!-- #docregion class-binding-3 -->
<!-- #docregion class-binding-3a -->
<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>
<!-- #enddocregion class-binding-3a -->
<!-- binding to `class.special` trumps the class attribute -->
<div class="special"
[class.special]="!isSpecial">This one is not so special</div>
<!-- #enddocregion class-binding-3 -->
<div bind-class.special="isSpecial">This class binding is special too</div>
<a class="to-toc" href="#toc">top</a>
<!--style binding -->
<hr><h2 id="style-binding">Style Binding</h2>
<!-- #docregion style-binding-1 -->
<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
<!-- #enddocregion style-binding-1 -->
<!-- #docregion style-binding-2 -->
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</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 id="event-binding">Event Binding</h2>
<!-- #docregion event-binding-1 -->
<button (click)="onSave()">Save</button>
<!-- #enddocregion event-binding-1 -->
<!-- #docregion event-binding-2 -->
<button on-click="onSave()">On Save</button>
<!-- #enddocregion event-binding-2 -->
<div>
<!-- #docregion event-binding-3 -->
<!-- `myClick` is an event on the custom `ClickDirective` -->
<!-- #docregion myClick -->
<div (myClick)="clickMessage=$event" clickable>click with myClick</div>
<!-- #enddocregion myClick -->
<!-- #enddocregion event-binding-3 -->
{{clickMessage}}
</div>
<!-- binding to a nested component -->
<!-- #docregion event-binding-to-component -->
<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>
<!-- #enddocregion event-binding-to-component -->
<br>
<big-hero-detail
(deleteRequest)="deleteHero($event)"
[hero]="currentHero">
</big-hero-detail>
<!-- #docregion event-binding-bubbling -->
<div class="parent-div" (click)="onClickMe($event)" clickable>Click me
<div class="child-div">Click me too!</div>
</div>
<!-- #enddocregion event-binding-bubbling -->
<!-- #docregion event-binding-no-propagation -->
<!-- Will save only once -->
<div (click)="onSave()" clickable>
<button (click)="onSave($event)">Save, no propagation</button>
</div>
<!-- #enddocregion event-binding-no-propagation -->
<!-- #docregion event-binding-propagation -->
<!-- Will save twice -->
<div (click)="onSave()" clickable>
<button (click)="onSave()">Save w/ propagation</button>
</div>
<!-- #enddocregion event-binding-propagation -->
<a class="to-toc" href="#toc">top</a>
<hr><h2 id="two-way">Two-way Binding</h2>
<div id="two-way-1">
<!-- #docregion two-way-1 -->
<my-sizer [(size)]="fontSizePx"></my-sizer>
<div [style.font-size.px]="fontSizePx">Resizable Text</div>
<!-- #enddocregion two-way-1 -->
<label>FontSize (px): <input [(ngModel)]="fontSizePx"></label>
</div>
<br>
<div id="two-way-2">
<h3>De-sugared two-way binding</h3>
<!-- #docregion two-way-2 -->
<my-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></my-sizer>
<!-- #enddocregion two-way-2 -->
</div>
<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 id="ngModel">NgModel (two-way) Binding</h2>
<h3>Result: {{currentHero.name}}</h3>
<!-- #docregion without-NgModel -->
<input [value]="currentHero.name"
(input)="currentHero.name=$event.target.value" >
<!-- #enddocregion without-NgModel -->
without NgModel
<br>
<!-- #docregion NgModel-1 -->
<input [(ngModel)]="currentHero.name">
<!-- #enddocregion NgModel-1 -->
[(ngModel)]
<br>
<!-- #docregion NgModel-2 -->
<input bindon-ngModel="currentHero.name">
<!-- #enddocregion NgModel-2 -->
bindon-ngModel
<br>
<!-- #docregion NgModel-3 -->
<input
[ngModel]="currentHero.name"
(ngModelChange)="currentHero.name=$event">
<!-- #enddocregion NgModel-3 -->
(ngModelChange)="...name=$event"
<br>
<!-- #docregion NgModel-4 -->
<input
[ngModel]="currentHero.name"
(ngModelChange)="setUppercaseName($event)">
<!-- #enddocregion NgModel-4 -->
(ngModelChange)="setUppercaseName($event)"
<a class="to-toc" href="#toc">top</a>
<!-- NgClass binding -->
<hr><h2 id="ngClass">NgClass Binding</h2>
<p>currentClasses is {{currentClasses | json}}</p>
<!-- #docregion NgClass-1 -->
<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div>
<!-- #enddocregion NgClass-1 -->
<!-- not used in chapter -->
<br>
<label>saveable <input type="checkbox" [(ngModel)]="canSave"></label> |
<label>modified: <input type="checkbox" [value]="!isUnchanged" (change)="isUnchanged=!isUnchanged"></label> |
<label>special: <input type="checkbox" [(ngModel)]="isSpecial"></label>
<button (click)="setCurrentClasses()">Refresh currentClasses</button>
<br><br>
<div [ngClass]="currentClasses">
This div should be {{ canSave ? "": "not"}} saveable,
{{ isUnchanged ? "unchanged" : "modified" }} and,
{{ isSpecial ? "": "not"}} special after clicking "Refresh".</div>
<br><br>
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
<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 id="ngStyle">NgStyle Binding</h2>
<!-- #docregion NgStyle-1 -->
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
This div is x-large or smaller.
</div>
<!-- #enddocregion NgStyle-1 -->
<h3>[ngStyle] binding to currentStyles - CSS property names</h3>
<p>currentStyles is {{currentStyles | json}}</p>
<!-- #docregion NgStyle-2 -->
<div [ngStyle]="currentStyles">
This div is initially italic, normal weight, and extra large (24px).
</div>
<!-- #enddocregion NgStyle-2 -->
<!-- not used in chapter -->
<br>
<label>italic: <input type="checkbox" [(ngModel)]="canSave"></label> |
<label>normal: <input type="checkbox" [(ngModel)]="isUnchanged"></label> |
<label>xlarge: <input type="checkbox" [(ngModel)]="isSpecial"></label>
<button (click)="setCurrentStyles()">Refresh currentStyles</button>
<br><br>
<div [ngStyle]="currentStyles">
This div should be {{ canSave ? "italic": "plain"}},
{{ isUnchanged ? "normal weight" : "bold" }} and,
{{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".</div>
<a class="to-toc" href="#toc">top</a>
<!-- NgIf binding -->
<hr><h2 id="ngIf">NgIf Binding</h2>
<!-- #docregion NgIf-1 -->
<hero-detail *ngIf="isActive"></hero-detail>
<!-- #enddocregion NgIf-1 -->
<!-- #docregion NgIf-2 -->
<div *ngIf="currentHero">Hello, {{currentHero.name}}</div>
<div *ngIf="nullHero">Hello, {{nullHero.name}}</div>
<!-- #enddocregion NgIf-2 -->
<!-- NgIf binding with template (no *) -->
<template [ngIf]="currentHero">Add {{currentHero.name}} with template</template>
<!-- Does not show because isActive is false! -->
<div>Hero Detail removed from DOM (via template) because isActive is false</div>
<template [ngIf]="isActive">
<hero-detail></hero-detail>
</template>
<!-- #docregion NgIf-3 -->
<!-- isSpecial is true -->
<div [class.hidden]="!isSpecial">Show with class</div>
<div [class.hidden]="isSpecial">Hide with class</div>
<!-- HeroDetail is in the DOM but hidden -->
<hero-detail [class.hidden]="isSpecial"></hero-detail>
<div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div>
<div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
<!-- #enddocregion NgIf-3 -->
<a class="to-toc" href="#toc">top</a>
<!-- NgFor binding -->
<hr><h2 id="ngFor">NgFor Binding</h2>
<div class="box">
<!-- #docregion NgFor-1, NgFor-1-2 -->
<div *ngFor="let hero of heroes">{{hero.name}}</div>
<!-- #enddocregion NgFor-1, NgFor-1-2 -->
</div>
<br>
<div class="box">
<!-- *ngFor w/ hero-detail Component -->
<!-- #docregion NgFor-2, NgFor-1-2 -->
<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>
<!-- #enddocregion NgFor-2, NgFor-1-2 -->
</div>
<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">
<!-- #docregion NgFor-3 -->
<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.name}}</div>
<!-- #enddocregion NgFor-3 -->
</div>
<p>with <i>comma</i> separator</p>
<div class="box">
<!-- Ex: "1 - Hercules" -->
<div *ngFor="let hero of heroes, let i=index">{{i + 1}} - {{hero.name}}</div>
</div>
<a class="to-toc" href="#toc">top</a>
<h4 id="ngFor-trackBy">*ngFor trackBy</h4>
<button (click)="resetHeroes()">Reset heroes</button>
<button (click)="changeIds()">Change ids</button>
<button (click)="clearTrackByCounts()">Clear counts</button>
<p><i>without</i> trackBy</p>
<div class="box">
<div #noTrackBy *ngFor="let hero of heroes">({{hero.id}}) {{hero.name}}</div>
<div id="noTrackByCnt" *ngIf="heroesNoTrackByCount" >
Hero DOM elements change #{{heroesNoTrackByCount}} without trackBy
</div>
</div>
<p>with trackBy</p>
<div class="box">
<div #withTrackBy *ngFor="let hero of heroes; trackBy: trackByHeroes">({{hero.id}}) {{hero.name}}</div>
<div id="withTrackByCnt" *ngIf="heroesWithTrackByCount">
Hero DOM elements change #{{heroesWithTrackByCount}} with trackBy
</div>
</div>
<br><br><br>
<p>with trackBy and <i>semi-colon</i> separator</p>
<div class="box">
<!-- #docregion trackBy -->
<div *ngFor="let hero of heroes; trackBy: trackByHeroes">
({{hero.id}}) {{hero.name}}
</div>
<!-- #enddocregion trackBy -->
</div>
<p>with trackBy and <i>comma</i> separator</p>
<div class="box">
<div *ngFor="let hero of heroes, trackBy: trackByHeroes">({{hero.id}}) {{hero.name}}</div>
</div>
<p>with trackBy and <i>space</i> separator</p>
<div class="box">
<div *ngFor="let hero of heroes trackBy: trackByHeroes">({{hero.id}}) {{hero.name}}</div>
</div>
<p>with <i>generic</i> trackById function</p>
<div class="box">
<div *ngFor="let hero of heroes, trackBy: trackById">({{hero.id}}) {{hero.name}}</div>
</div>
<a class="to-toc" href="#toc">top</a>
<!-- NgSwitch binding -->
<hr><h2 id="ngSwitch">NgSwitch Binding</h2>
<p>Pick your favorite hero</p>
<div>
<label *ngFor="let h of heroes">
<input type="radio" name="heroes" [(ngModel)]="currentHero" [value]="h">{{h.name}}
</label>
</div>
<!-- #docregion NgSwitch -->
<div [ngSwitch]="currentHero.emotion">
<happy-hero *ngSwitchCase="'happy'" [hero]="currentHero"></happy-hero>
<sad-hero *ngSwitchCase="'sad'" [hero]="currentHero"></sad-hero>
<confused-hero *ngSwitchCase="'confused'" [hero]="currentHero"></confused-hero>
<!-- #enddocregion NgSwitch -->
<!-- #docregion NgSwitch-div -->
<div *ngSwitchCase="'confused'">Are you as confused as {{currentHero.name}}?</div>
<!-- #enddocregion NgSwitch-div -->
<!-- #docregion NgSwitch -->
<unknown-hero *ngSwitchDefault [hero]="currentHero"></unknown-hero>
</div>
<!-- #enddocregion NgSwitch -->
<a class="to-toc" href="#toc">top</a>
<!-- template reference variable -->
<hr><h2 id="ref-vars">Template reference variables</h2>
<!-- #docregion ref-phone -->
<!-- #docregion ref-var -->
<input #phone placeholder="phone number">
<!-- #enddocregion ref-var -->
<!-- lots of other elements -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>
<!-- #enddocregion ref-phone -->
<!-- #docregion ref-fax -->
<input ref-fax placeholder="fax number">
<button (click)="callFax(fax.value)">Fax</button>
<!-- #enddocregion ref-fax -->
<!-- btn refers to the button element; show its disabled state -->
<button #btn disabled [innerHTML]="'disabled by attribute: '+btn.disabled"></button>
<h4>Example Form</h4>
<hero-form [hero]="currentHero"></hero-form>
<a class="to-toc" href="#toc">top</a>
<!-- inputs and output -->
<hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2>
<!-- #docregion io-1 -->
<img [src]="iconUrl"/>
<button (click)="onSave()">Save</button>
<!-- #enddocregion io-1 -->
<!-- #docregion io-2 -->
<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)">
</hero-detail>
<!-- #enddocregion io-2 -->
<div (myClick)="clickMessage2=$event" clickable>myClick2</div>
{{clickMessage2}}
<a class="to-toc" href="#toc">top</a>
<!-- Pipes -->
<hr><h2 id="pipes">Pipes</h2>
<!-- #docregion pipes-1 -->
<div>Title through uppercase pipe: {{title | uppercase}}</div>
<!-- #enddocregion pipes-1 -->
<!-- #docregion pipes-2 -->
<!-- Pipe chaining: convert title to uppercase, then to lowercase -->
<div>
Title through a pipe chain:
{{title | uppercase | lowercase}}
</div>
<!-- #enddocregion pipes-2 -->
<!-- #docregion pipes-3 -->
<!-- pipe with configuration argument => "February 25, 1970" -->
<div>Birthdate: {{currentHero?.birthdate | date:'longDate'}}</div>
<!-- #enddocregion pipes-3 -->
<!-- #docregion pipes-json -->
<div>{{currentHero | json}}</div>
<!-- #enddocregion pipes-json -->
<div>Birthdate: {{(currentHero?.birthdate | date:'longDate') | uppercase}}</div>
<div>
<!-- pipe price to USD and display the $ symbol -->
<label>Price: </label>{{product.price | currency:'USD':true}}
</div>
<a class="to-toc" href="#toc">top</a>
<!-- Null values and the safe navigation operator -->
<hr><h2 id="safe-navigation-operator">Safe navigation operator <i>?.</i></h2>
<div>
<!-- #docregion safe-1 -->
The title is {{title}}
<!-- #enddocregion safe-1 -->
</div>
<div>
<!-- #docregion safe-2 -->
The current hero's name is {{currentHero?.name}}
<!-- #enddocregion safe-2 -->
</div>
<div>
<!-- #docregion safe-3 -->
The current hero's name is {{currentHero.name}}
<!-- #enddocregion safe-3 -->
</div>
<!--
The null hero's name is {{nullHero.name}}
See console log:
TypeError: Cannot read property 'name' of null in [null]
-->
<!-- #docregion safe-4 -->
<!--No hero, div not displayed, no error -->
<div *ngIf="nullHero">The null hero's name is {{nullHero.name}}</div>
<!-- #enddocregion safe-4 -->
<div>
<!-- #docregion safe-5 -->
The null hero's name is {{nullHero && nullHero.name}}
<!-- #enddocregion safe-5 -->
</div>
<div>
<!-- #docregion safe-6 -->
<!-- No hero, no problem! -->
The null hero's name is {{nullHero?.name}}
<!-- #enddocregion safe-6 -->
</div>
<a class="to-toc" href="#toc">top</a>
<!-- TODO: discuss this in the Style binding section -->
<!-- enums in bindings -->
<hr><h2 id="enums">Enums in binding</h2>
<p>
<!-- #docregion enums -->
The name of the Color.Red enum is {{Color[Color.Red]}}.<br>
The current color is {{Color[color]}} and its number is {{color}}.<br>
<button [style.color]="Color[color]" (click)="colorToggle()">Enum Toggle</button>
<!-- #enddocregion enums -->
</p>
<a class="to-toc" href="#toc">top</a>

View File

@ -0,0 +1,183 @@
/* tslint:disable:forin member-ordering */
// #docplaster
import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren } from '@angular/core';
import { Hero } from './hero';
export enum Color {Red, Green, Blue};
/**
* Giant grab bag of stuff to drive the chapter
*/
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit, OnInit {
ngOnInit() {
this.resetHeroes();
this.setCurrentClasses();
this.setCurrentStyles();
}
ngAfterViewInit() {
// Detect effects of NgForTrackBy
trackChanges(this.heroesNoTrackBy, () => this.heroesNoTrackByCount++);
trackChanges(this.heroesWithTrackBy, () => this.heroesWithTrackByCount++);
}
@ViewChildren('noTrackBy') heroesNoTrackBy: QueryList<ElementRef>;
@ViewChildren('withTrackBy') heroesWithTrackBy: QueryList<ElementRef>;
actionName = 'Go for it';
badCurly = 'bad curly';
classes = 'special';
help = '';
alert(msg?: string) { window.alert(msg); }
callFax(value: string) { this.alert(`Faxing ${value} ...`); }
callPhone(value: string) { this.alert(`Calling ${value} ...`); }
canSave = true;
changeIds() {
this.resetHeroes();
this.heroes.forEach(h => h.id += 10 * this.heroIdIncrement++);
this.heroesWithTrackByCountReset = -1;
}
clearTrackByCounts() {
const trackByCountReset = this.heroesWithTrackByCountReset;
this.resetHeroes();
this.heroesNoTrackByCount = -1;
this.heroesWithTrackByCount = trackByCountReset;
this.heroIdIncrement = 1;
}
clicked = '';
clickMessage = '';
clickMessage2 = '';
Color = Color;
color = Color.Red;
colorToggle() {this.color = (this.color === Color.Red) ? Color.Blue : Color.Red; }
currentHero: Hero;
deleteHero(hero: Hero) {
this.alert(`Delete ${hero ? hero.name : 'the hero'}.`);
}
// #docregion evil-title
evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
// #enddocregion evil-title
fontSizePx = 16;
title = 'Template Syntax';
getVal(): number { return 2; }
name: string = Hero.heroes[0].name;
hero: Hero; // defined to demonstrate template context precedence
heroes: Hero[];
// trackBy change counting
heroesNoTrackByCount = 0;
heroesWithTrackByCount = 0;
heroesWithTrackByCountReset = 0;
heroIdIncrement = 1;
// heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
// Public Domain terms of use: http://www.wpclipart.com/terms.html
heroImageUrl = 'images/hero.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
villainImageUrl = 'images/villain.png';
iconUrl = 'images/ng-logo.png';
isActive = false;
isSpecial = true;
isUnchanged = true;
get nullHero(): Hero { return null; }
onClickMe(event: KeyboardEvent) {
let evtMsg = event ? ' Event target class is ' + (<HTMLElement>event.target).className : '';
this.alert('Click me.' + evtMsg);
}
onSave(event: KeyboardEvent) {
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).innerText : '';
this.alert('Saved.' + evtMsg);
if (event) { event.stopPropagation(); }
}
onSubmit() {/* referenced but not used */}
product = {
name: 'frimfram',
price: 42
};
// updates with fresh set of cloned heroes
resetHeroes() {
this.heroes = Hero.heroes.map(hero => hero.clone());
this.currentHero = this.heroes[0];
this.heroesWithTrackByCountReset = 0;
}
setUppercaseName(name: string) {
this.currentHero.name = name.toUpperCase();
}
// #docregion setClasses
currentClasses: {};
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial
};
}
// #enddocregion setClasses
// #docregion setStyles
currentStyles: {};
setCurrentStyles() {
// CSS styles: set per current state of component properties
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px'
};
}
// #enddocregion setStyles
// #docregion trackByHeroes
trackByHeroes(index: number, hero: Hero): number { return hero.id; }
// #enddocregion trackByHeroes
// #docregion trackById
trackById(index: number, item: any): number { return item['id']; }
// #enddocregion trackById
}
// helper to track changes to viewChildren
function trackChanges(views: QueryList<ElementRef>, changed: () => void) {
let oldRefs = views.toArray();
views.changes.subscribe((changes: QueryList<ElementRef>) => {
const changedRefs = changes.toArray();
// Is every changed ElemRef the same as old and in the same position
const isSame = oldRefs.every((v, i) => v === changedRefs[i]);
if (!isSame) {
oldRefs = changedRefs;
// wait a tick because called after views are constructed
setTimeout(changed, 0);
}
});
}

View File

@ -0,0 +1,15 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular
/* Other imports */
@NgModule({
imports: [
BrowserModule,
FormsModule // <--- import into the NgModule
],
/* Other module metadata */
})
export class AppModule { }

View File

@ -0,0 +1,29 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { BigHeroDetailComponent, HeroDetailComponent } from './hero-detail.component';
import { ClickDirective, ClickDirective2 } from './click.directive';
import { HeroFormComponent } from './hero-form.component';
import { heroSwitchComponents } from './hero-switch.components';
import { SizerComponent } from './sizer.component';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent,
BigHeroDetailComponent,
HeroDetailComponent,
HeroFormComponent,
heroSwitchComponents,
ClickDirective,
ClickDirective2,
SizerComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -0,0 +1,41 @@
/* tslint:disable use-output-property-decorator */
// #docplaster
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';
@Directive({selector: '[myClick]'})
export class ClickDirective {
// #docregion output-myClick
@Output('myClick') clicks = new EventEmitter<string>(); // @Output(alias) propertyName = ...
// #enddocregion output-myClick
toggle = false;
constructor(el: ElementRef) {
el.nativeElement
.addEventListener('click', (event: Event) => {
this.toggle = !this.toggle;
this.clicks.emit(this.toggle ? 'Click!' : '');
});
}
}
// #docregion output-myClick2
@Directive({
// #enddocregion output-myClick2
selector: '[myClick2]',
// #docregion output-myClick2
outputs: ['clicks:myClick'] // propertyName:alias
})
// #enddocregion output-myClick2
export class ClickDirective2 {
clicks = new EventEmitter<string>();
toggle = false;
constructor(el: ElementRef) {
el.nativeElement
.addEventListener('click', (event: Event) => {
this.toggle = !this.toggle;
this.clicks.emit(this.toggle ? 'Click2!' : '');
});
}
}

View File

@ -0,0 +1,80 @@
/* tslint:disable use-input-property-decorator use-output-property-decorator */
// #docplaster
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Hero } from './hero';
// #docregion input-output-2
@Component({
// #enddocregion input-output-2
selector: 'hero-detail',
// #docregion input-output-2
inputs: ['hero'],
outputs: ['deleteRequest'],
// #enddocregion input-output-2
styles: ['button {margin-left: 8px} div {margin: 8px 0} img {height:24px}'],
// #docregion template-1
template: `
<div>
<img src="{{heroImageUrl}}">
<span [style.text-decoration]="lineThrough">
{{prefix}} {{hero?.name}}
</span>
<button (click)="delete()">Delete</button>
</div>`
// #enddocregion template-1
// #docregion input-output-2
})
// #enddocregion input-output-2
export class HeroDetailComponent {
hero: Hero = new Hero(-1, '', 'Zzzzzzzz'); // default sleeping hero
// heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
// Public Domain terms of use: http://www.wpclipart.com/terms.html
heroImageUrl = 'images/hero.png';
lineThrough = '';
@Input() prefix = '';
// #docregion deleteRequest
// This component make a request but it can't actually delete a hero.
deleteRequest = new EventEmitter<Hero>();
delete() {
this.deleteRequest.emit(this.hero);
// #enddocregion deleteRequest
this.lineThrough = this.lineThrough ? '' : 'line-through';
// #docregion deleteRequest
}
// #enddocregion deleteRequest
}
@Component({
selector: 'big-hero-detail',
template: `
<div class="detail">
<img src="{{heroImageUrl}}">
<div><b>{{hero?.name}}</b></div>
<div>Name: {{hero?.name}}</div>
<div>Emotion: {{hero?.emotion}}</div>
<div>Birthdate: {{hero?.birthdate | date:'longDate'}}</div>
<div>Web: <a href="{{hero?.url}}" target="_blank">{{hero?.url}}</a></div>
<div>Rate/hr: {{hero?.rate | currency:'EUR'}}</div>
<br clear="all">
<button (click)="delete()">Delete</button>
</div>
`,
styles: [`
.detail { border: 1px solid black; padding: 4px; max-width: 450px; }
img { float: left; margin-right: 8px; height: 100px; }
`]
})
export class BigHeroDetailComponent extends HeroDetailComponent {
// #docregion input-output-1
@Input() hero: Hero;
@Output() deleteRequest = new EventEmitter<Hero>();
// #enddocregion input-output-1
delete() {
this.deleteRequest.emit(this.hero);
}
}

View File

@ -0,0 +1,15 @@
<div id="heroForm">
<!-- #docregion -->
<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm">
<div class="form-group">
<label for="name">Name
<input class="form-control" name="name" required [(ngModel)]="hero.name">
</label>
</div>
<button type="submit" [disabled]="!heroForm.form.valid">Submit</button>
</form>
<div [hidden]="!heroForm.form.valid">
{{submitMessage}}
</div>
<!-- #enddocregion -->
</div>

View File

@ -0,0 +1,30 @@
import { Component, Input, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Hero } from './hero';
@Component({
selector: 'hero-form',
templateUrl: './hero-form.component.html',
styles: [`
button { margin: 6px 0; }
#heroForm { border: 1px solid black; margin: 20px 0; padding: 8px; max-width: 350px; }
`]
})
export class HeroFormComponent {
@Input() hero: Hero;
@ViewChild('heroForm') form: NgForm;
private _submitMessage = '';
get submitMessage() {
if (!this.form.valid) {
this._submitMessage = '';
}
return this._submitMessage;
}
onSubmit(form: NgForm) {
this._submitMessage = 'Submitted. form value is ' + JSON.stringify(form.value);
}
}

View File

@ -0,0 +1,42 @@
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
selector: 'happy-hero',
template: `Wow. You like {{hero.name}}. What a happy hero ... just like you.`
})
export class HappyHeroComponent {
@Input() hero: Hero;
}
@Component({
selector: 'sad-hero',
template: `You like {{hero.name}}? Such a sad hero. Are you sad too?`
})
export class SadHeroComponent {
@Input() hero: Hero;
}
@Component({
selector: 'confused-hero',
template: `Are you as confused as {{hero.name}}?`
})
export class ConfusedHeroComponent {
@Input() hero: Hero;
}
@Component({
selector: 'unknown-hero',
template: `{{message}}`
})
export class UnknownHeroComponent {
@Input() hero: Hero;
get message() {
return this.hero && this.hero.name ?
`${this.hero.name} is strange and mysterious.` :
'Are you feeling indecisive?';
}
}
export const heroSwitchComponents =
[ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];

View File

@ -0,0 +1,34 @@
export class Hero {
static nextId = 0;
static heroes: Hero[] = [
new Hero(
null,
'Hercules',
'happy',
new Date(1970, 1, 25),
'http://www.imdb.com/title/tt0065832/',
325
),
new Hero(1, 'Mr. Nice', 'happy'),
new Hero(2, 'Narco', 'sad' ),
new Hero(3, 'Windstorm', 'confused' ),
new Hero(4, 'Magneta')
];
constructor(
public id?: number,
public name?: string,
public emotion?: string,
public birthdate?: Date,
public url?: string,
public rate = 100,
) {
this.id = id ? id : Hero.nextId++;
}
clone(): Hero {
return Object.assign(new Hero(), this);
}
}

View File

@ -0,0 +1,24 @@
// #docregion
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'my-sizer',
template: `
<div>
<button (click)="dec()" title="smaller">-</button>
<button (click)="inc()" title="bigger">+</button>
<label [style.font-size.px]="size">FontSize: {{size}}px</label>
</div>`
})
export class SizerComponent {
@Input() size: number | string;
@Output() sizeChange = new EventEmitter<number>();
dec() { this.resize(-1); }
inc() { this.resize(+1); }
resize(delta: number) {
this.size = Math.min(40, Math.max(8, +this.size + delta));
this.sizeChange.emit(this.size);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title>Template Syntax</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>

View File

@ -0,0 +1,5 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);