docs(aio): update migrated content from anguar.io
This commit is contained in:

committed by
Pete Bacon Darwin

parent
ff82756415
commit
fd72fad8fd
@ -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}
|
820
aio/content/examples/template-syntax/src/app/app.component.html
Normal file
820
aio/content/examples/template-syntax/src/app/app.component.html
Normal 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 ({{title}}, [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>
|
183
aio/content/examples/template-syntax/src/app/app.component.ts
Normal file
183
aio/content/examples/template-syntax/src/app/app.component.ts
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
15
aio/content/examples/template-syntax/src/app/app.module.1.ts
Normal file
15
aio/content/examples/template-syntax/src/app/app.module.1.ts
Normal 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 { }
|
29
aio/content/examples/template-syntax/src/app/app.module.ts
Normal file
29
aio/content/examples/template-syntax/src/app/app.module.ts
Normal 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 { }
|
@ -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!' : '');
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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>
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 ];
|
34
aio/content/examples/template-syntax/src/app/hero.ts
Normal file
34
aio/content/examples/template-syntax/src/app/hero.ts
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
BIN
aio/content/examples/template-syntax/src/images/hero.png
Normal file
BIN
aio/content/examples/template-syntax/src/images/hero.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
BIN
aio/content/examples/template-syntax/src/images/ng-logo.png
Normal file
BIN
aio/content/examples/template-syntax/src/images/ng-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
BIN
aio/content/examples/template-syntax/src/images/villain.png
Normal file
BIN
aio/content/examples/template-syntax/src/images/villain.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
26
aio/content/examples/template-syntax/src/index.html
Normal file
26
aio/content/examples/template-syntax/src/index.html
Normal 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>
|
5
aio/content/examples/template-syntax/src/main.ts
Normal file
5
aio/content/examples/template-syntax/src/main.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
Reference in New Issue
Block a user