docs(Template Syntax): update to a.53 (kebab work)

closes #493
This commit is contained in:
Ward Bell 2015-12-13 20:10:03 -08:00
parent 31051d29f0
commit 7cf097ea68
14 changed files with 193 additions and 233 deletions

View File

@ -60,22 +60,23 @@
<!-- #docregion property-binding-syntax-1 --> <!-- #docregion property-binding-syntax-1 -->
<img [src] = "heroImageUrl"> <img [src] = "heroImageUrl">
<hero-detail [hero]="currentHero"></hero-detail> <hero-detail [hero]="currentHero"></hero-detail>
<div [ng-class] = "{selected: isSelected}"></div> <div [ngClass] = "{selected: isSelected}"></div>
<!-- #enddocregion property-binding-syntax-1 --> <!-- #enddocregion property-binding-syntax-1 -->
</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 my-click (my-click)="clicked=$event">click me</div> <div myClick (myClick)="clicked=$event">click me</div>
<!-- #enddocregion event-binding-syntax-1 --> <!-- #enddocregion event-binding-syntax-1 -->
{{clicked}} {{clicked}}
<br><br> <br><br>
<div> <div>
<!-- #docregion 2-way-binding-syntax-1 --> <!-- #docregion 2-way-binding-syntax-1 -->
<input [(ng-model)]="heroName"> <input [(ngModel)]="heroName">
<!-- #enddocregion 2-way-binding-syntax-1 --> <!-- #enddocregion 2-way-binding-syntax-1 -->
Hero Name: {{heroName}} Hero Name: {{heroName}}
</div> </div>
@ -96,20 +97,6 @@
<!-- #enddocregion style-binding-syntax-1 --> <!-- #enddocregion style-binding-syntax-1 -->
button</button> button</button>
<hr><h2>kebab-case</h2>
<!-- #docregion bad-mixed-case -->
<!-- BAD: mixed case target names don't work
<label [textContent] = "title"></label>
<hero-detail [isActive] = "isActive"></hero-detail>
-->
<!-- #enddocregion bad-mixed-case -->
<!-- #docregion kebab-case -->
<!-- lower kebab-case -->
<label [text-content] = "title"></label>
<hero-detail [is-active] = "isActive"></hero-detail>
<!-- #enddocregion kebab-case -->
<!-- property vs. attribute --> <!-- property vs. attribute -->
<hr><h2>Property vs. Attribute (img examples)</h2> <hr><h2>Property vs. Attribute (img examples)</h2>
<!-- examine the following <img> tag in the browser tools --> <!-- examine the following <img> tag in the browser tools -->
@ -148,7 +135,7 @@ button</button>
<button [disabled]="isUnchanged">Cancel</button> <button [disabled]="isUnchanged">Cancel</button>
<!-- #enddocregion property-binding-2 --> <!-- #enddocregion property-binding-2 -->
<!-- #docregion property-binding-3 --> <!-- #docregion property-binding-3 -->
<div [ng-class]="'special'">NgClass is special</div> <div [ngClass]="'special'">NgClass is special</div>
<!-- #enddocregion property-binding-3 --> <!-- #enddocregion property-binding-3 -->
<!-- #docregion property-binding-4 --> <!-- #docregion property-binding-4 -->
<hero-detail [hero]="selectedHero"></hero-detail> <hero-detail [hero]="selectedHero"></hero-detail>
@ -162,13 +149,13 @@ button</button>
<hero-detail hero="…what do we do here??? …"></hero-detail> <hero-detail hero="…what do we do here??? …"></hero-detail>
<!-- #enddocregion property-binding-6 --> <!-- #enddocregion property-binding-6 -->
<!-- #docregion property-binding-v-interpolation --> <!-- #docregion property-binding-vs-interpolation -->
<img src="{{heroImageUrl}}"> <img src="{{heroImageUrl}}">
<img [src]="'' + heroImageUrl"> <img [src]="'' + heroImageUrl">
<div>The title is {{title}}</div> <div>The title is {{title}}</div>
<div [text-content]="'The title is '+title"></div> <div [textContent]="'The title is '+title"></div>
<!-- #enddocregion property-binding-v-interpolation --> <!-- #enddocregion property-binding-vs-interpolation -->
<!-- attribute binding --> <!-- attribute binding -->
<hr><h2>Attribute Binding</h2> <hr><h2>Attribute Binding</h2>
@ -246,12 +233,12 @@ button</button>
<!-- #docregion style-binding-1 --> <!-- #docregion style-binding-1 -->
<button [style.color] = "isSpecial ? 'red' : 'green'">Red</button> <button [style.color] = "isSpecial ? 'red' : 'green'">Red</button>
<button [style.background-color]="canSave ?'cyan' : 'grey'" >Save</button> <button [style.backgroundColor]="canSave ?'cyan' : 'grey'" >Save</button>
<!-- #enddocregion style-binding-1 --> <!-- #enddocregion style-binding-1 -->
<!-- #docregion style-binding-2 --> <!-- #docregion style-binding-2 -->
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button> <button [style.fontSize.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button> <button [style.fontSize.%]="!isSpecial ? 150 : 50" >Small</button>
<!-- #enddocregion style-binding-2 --> <!-- #enddocregion style-binding-2 -->
<!-- event binding --> <!-- event binding -->
@ -267,8 +254,8 @@ button</button>
<div> <div>
<!-- #docregion event-binding-3 --> <!-- #docregion event-binding-3 -->
<!-- `my-click` is an event on the custom `MyClickDirective` --> <!-- `myClick` is an event on the custom `MyClickDirective` -->
<div my-click (my-click)="clickity=$event">click with my-click</div> <div myClick (myClick)="clickity=$event">click with myClick</div>
<!-- #enddocregion event-binding-3 --> <!-- #enddocregion event-binding-3 -->
{{clickity}} {{clickity}}
</div> </div>
@ -322,28 +309,28 @@ button</button>
without NgModel without NgModel
<br> <br>
<!-- #docregion NgModel-1 --> <!-- #docregion NgModel-1 -->
<input [(ng-model)]="currentHero.firstName"> <input [(ngModel)]="currentHero.firstName">
<!-- #enddocregion NgModel-1 --> <!-- #enddocregion NgModel-1 -->
[(ng-model)] [(ngModel)]
<br> <br>
<!-- #docregion NgModel-2 --> <!-- #docregion NgModel-2 -->
<input bindon-ng-model="currentHero.firstName"> <input bindon-ngModel="currentHero.firstName">
<!-- #enddocregion NgModel-2 --> <!-- #enddocregion NgModel-2 -->
bindon-ng-model bindon-ngModel
<br> <br>
<!-- #docregion NgModel-3 --> <!-- #docregion NgModel-3 -->
<input <input
[ng-model]="currentHero.firstName" [ngModel]="currentHero.firstName"
(ng-model-change)="currentHero.firstName=$event"> (ngModelChange)="currentHero.firstName=$event">
<!-- #enddocregion NgModel-3 --> <!-- #enddocregion NgModel-3 -->
(ng-model-change) = "...firstName=$event" (ngModelChange) = "...firstName=$event"
<br> <br>
<!-- #docregion NgModel-4 --> <!-- #docregion NgModel-4 -->
<input <input
[ng-model]="currentHero.firstName" [ngModel]="currentHero.firstName"
(ng-model-change)="setUpperCaseFirstName($event)"> (ngModelChange)="setUpperCaseFirstName($event)">
<!-- #enddocregion NgModel-4 --> <!-- #enddocregion NgModel-4 -->
(ng-model-change) = "setUpperCaseFirstName($event)" (ngModelChange) = "setUpperCaseFirstName($event)"
<br> <br>
@ -353,36 +340,36 @@ bindon-ng-model
<p>setClasses returns {{setClasses() | json}}</p> <p>setClasses returns {{setClasses() | json}}</p>
<!-- #docregion NgClass-1 --> <!-- #docregion NgClass-1 -->
<div [ng-class]="setClasses()">This div is saveable and special</div> <div [ngClass]="setClasses()">This div is saveable and special</div>
<!-- #enddocregion NgClass-1 --> <!-- #enddocregion NgClass-1 -->
<div [ng-class]="setClasses()" #class-div> <div [ngClass]="setClasses()" #classDiv>
After setClasses(), the classes are "{{classDiv.className}}" After setClasses(), the classes are "{{classDiv.className}}"
</div> </div>
<!-- not used in chapter --> <!-- not used in chapter -->
<div [ng-class]="isSpecial ? 'special' : ''">This div is special</div> <div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
<div class="bad curly special">Bad curly special</div> <div class="bad curly special">Bad curly special</div>
<div [ng-class]="{bad:false, curly:true, special:true}">Curly special</div> <div [ngClass]="{bad:false, curly:true, special:true}">Curly special</div>
<!-- NgStyle binding --> <!-- NgStyle binding -->
<hr><h2>NgStyle Binding</h2> <hr><h2>NgStyle Binding</h2>
<!-- #docregion NgStyle-1 --> <!-- #docregion NgStyle-1 -->
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" > <div [style.fontSize]="isSpecial ? 'x-large' : 'smaller'" >
This div is x-large This div is x-large
</div> </div>
<!-- #enddocregion NgStyle-1 --> <!-- #enddocregion NgStyle-1 -->
<p>setStyles returns {{setStyles() | json}}</p> <p>setStyles returns {{setStyles() | json}}</p>
<!-- #docregion NgStyle-2 --> <!-- #docregion NgStyle-2 -->
<div [ng-style]="setStyles()"> <div [ngStyle]="setStyles()">
This div is italic, normal weight, and x-large This div is italic, normal weight, and x-large
</div> </div>
<!-- #enddocregion NgStyle-2 --> <!-- #enddocregion NgStyle-2 -->
<div [ng-style]="setStyles()" #style-div> <div [ngStyle]="setStyles()" #styleDiv>
After setStyles(), the styles are "{{getStyles(styleDiv)}}" After setStyles(), the styles are "{{getStyles(styleDiv)}}"
</div> </div>
@ -394,26 +381,26 @@ After setClasses(), the classes are "{{classDiv.className}}"
<hr><h2>NgIf Binding</h2> <hr><h2>NgIf Binding</h2>
<!-- #docregion NgIf-1 --> <!-- #docregion NgIf-1 -->
<div *ng-if="currentHero">Hello, {{currentHero.firstName}}</div> <div *ngIf="currentHero">Hello, {{currentHero.firstName}}</div>
<!-- #enddocregion NgIf-1 --> <!-- #enddocregion NgIf-1 -->
<!-- #docregion NgIf-2 --> <!-- #docregion NgIf-2 -->
<!-- not displayed because nullHero is falsey. <!-- not displayed because nullHero is falsey.
`nullHero.firstName` never has a chance to fail --> `nullHero.firstName` never has a chance to fail -->
<div *ng-if="nullHero">Hello, {{nullHero.firstName}}</div> <div *ngIf="nullHero">Hello, {{nullHero.firstName}}</div>
<!-- Hero Detail is not in the DOM because isActive is false--> <!-- Hero Detail is not in the DOM because isActive is false-->
<hero-detail *ng-if="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 [ng-if]="currentHero">Add {{currentHero.firstName}} with template</template> <template [ngIf]="currentHero">Add {{currentHero.firstName}} with template</template>
<!-- Does not show because isActive is false! --> <!-- Does not show because isActive is false! -->
<div>Hero Detail removed from DOM (via template) because isActive is false</div> <div>Hero Detail removed from DOM (via template) because isActive is false</div>
<template [ng-if]="isActive"> <template [ngIf]="isActive">
<hero-detail></hero-detail> <hero-detail></hero-detail>
</template> </template>
@ -434,7 +421,7 @@ After setClasses(), the classes are "{{classDiv.className}}"
<!-- NgSwitch binding --> <!-- NgSwitch binding -->
<hr><h2>NgSwitch Binding</h2> <hr><h2>NgSwitch Binding</h2>
<fieldset #toe-picker (click)="null" > <fieldset #toePicker (click)="null" >
<input type="radio" name="toes" value="Eenie">Eenie <input type="radio" name="toes" value="Eenie">Eenie
<input type="radio" name="toes" checked value="Meanie">Meanie <input type="radio" name="toes" checked value="Meanie">Meanie
<input type="radio" name="toes" value="Miney">Miney <input type="radio" name="toes" value="Miney">Miney
@ -443,12 +430,12 @@ After setClasses(), the classes are "{{classDiv.className}}"
</fieldset> </fieldset>
<!-- #docregion NgSwitch --> <!-- #docregion NgSwitch -->
<div class="toe">You picked <div class="toe">You picked
<span [ng-switch]="toeChoice(toePicker)"> <span [ngSwitch]="toeChoice(toePicker)">
<template [ng-switch-when]="'Eenie'">Eenie</template> <template [ngSwitchWhen]="'Eenie'">Eenie</template>
<template [ng-switch-when]="'Meanie'">Meanie</template> <template [ngSwitchWhen]="'Meanie'">Meanie</template>
<template [ng-switch-when]="'Miney'">Miney</template> <template [ngSwitchWhen]="'Miney'">Miney</template>
<template [ng-switch-when]="'Moe'">Moe</template> <template [ngSwitchWhen]="'Moe'">Moe</template>
<template ng-switch-default>Other</template> <template ngSwitchDefault>Other</template>
</span> </span>
</div> </div>
<!-- #enddocregion NgSwitch --> <!-- #enddocregion NgSwitch -->
@ -459,15 +446,15 @@ After setClasses(), the classes are "{{classDiv.className}}"
<div class="box"> <div class="box">
<!-- #docregion NgFor-1 --> <!-- #docregion NgFor-1 -->
<div *ng-for="#hero of heroes">{{hero.fullName}}</div> <div *ngFor="#hero of heroes">{{hero.fullName}}</div>
<!-- #enddocregion NgFor-1 --> <!-- #enddocregion NgFor-1 -->
</div> </div>
<br> <br>
<div class="box"> <div class="box">
<!-- *ng-for w/ hero-detail Component --> <!-- *ngFor w/ hero-detail Component -->
<!-- #docregion NgFor-2 --> <!-- #docregion NgFor-2 -->
<hero-detail *ng-for="#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> <br>
@ -475,7 +462,7 @@ After setClasses(), the classes are "{{classDiv.className}}"
<div class="box"> <div class="box">
<!-- Ex: 1 - Hercules Son of Zeus --> <!-- Ex: 1 - Hercules Son of Zeus -->
<!-- #docregion NgFor-3 --> <!-- #docregion NgFor-3 -->
<div *ng-for="#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> <br>
@ -485,28 +472,28 @@ After setClasses(), the classes are "{{classDiv.className}}"
<h3>NgIf expansion</h3> <h3>NgIf expansion</h3>
<!-- #docregion Template-1 --> <!-- #docregion Template-1 -->
<hero-detail *ng-if="currentHero" [hero]="currentHero"></hero-detail> <hero-detail *ngIf="currentHero" [hero]="currentHero"></hero-detail>
<!-- #enddocregion Template-1 --> <!-- #enddocregion Template-1 -->
<!-- #docregion Template-2 --> <!-- #docregion Template-2 -->
<template [ng-if]="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>
<div class="box"> <div class="box">
<!-- ng-for 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="ng-for #hero of heroes" [hero]="hero"></hero-detail> <hero-detail template="ngFor #hero of heroes" [hero]="hero"></hero-detail>
<!-- #enddocregion Template-3 --> <!-- #enddocregion Template-3 -->
</div> </div>
<br> <br>
<div class="box"> <div class="box">
<!-- ng-for w/ hero-detail Component inside a template element --> <!-- ngFor w/ hero-detail Component inside a template element -->
<!-- #docregion Template-4 --> <!-- #docregion Template-4 -->
<template ng-for #hero [ng-for-of]="heroes"> <template ngFor #hero [ngForOf]="heroes">
<hero-detail [hero]="hero"></hero-detail> <hero-detail [hero]="hero"></hero-detail>
</template> </template>
<!-- #enddocregion Template-4 --> <!-- #enddocregion Template-4 -->
@ -530,12 +517,12 @@ After setClasses(), the classes are "{{classDiv.className}}"
<h4>Example Form</h4> <h4>Example Form</h4>
<!-- #docregion var-form --> <!-- #docregion var-form -->
<!-- #docregion var-form-a --> <!-- #docregion var-form-a -->
<form (ng-submit)="onSubmit(theForm)" #theForm="form"> <form (ngSubmit)="onSubmit(theForm)" #theForm="ngForm">
<!-- #enddocregion var-form-a --> <!-- #enddocregion var-form-a -->
<div class="form-group"> <div class="form-group">
<label for="name">Name</label> <label for="name">Name</label>
<input class="form-control" required ng-control="firstName" <input class="form-control" required ngControl="firstName"
[(ng-model)]="currentHero.firstName"> [(ngModel)]="currentHero.firstName">
</div> </div>
<!-- #docregion var-form-a --> <!-- #docregion var-form-a -->
<button type="submit" [disabled]="!theForm.form.valid">Submit</button> <button type="submit" [disabled]="!theForm.form.valid">Submit</button>
@ -545,7 +532,7 @@ After setClasses(), the classes are "{{classDiv.className}}"
<br><br> <br><br>
<!-- btn refers to the button element; show its disabled state --> <!-- btn refers to the button element; show its disabled state -->
<button #btn disabled [text-content]="'disabled by attribute: '+btn.disabled"></button> <button #btn disabled [textContent]="'disabled by attribute: '+btn.disabled"></button>
<!-- inputs and output --> <!-- inputs and output -->
<hr><h2>Inputs and Outputs</h2> <hr><h2>Inputs and Outputs</h2>
@ -560,7 +547,7 @@ After setClasses(), the classes are "{{classDiv.className}}"
</hero-detail> </hero-detail>
<!-- #enddocregion io-2 --> <!-- #enddocregion io-2 -->
<div my-click2 (my-click)="clicked2=$event">my-click2</div> <div myClick2 (myClick)="clicked2=$event">myClick2</div>
{{clicked2}} {{clicked2}}
<!-- Pipes --> <!-- Pipes -->
@ -632,7 +619,7 @@ See console log
<!-- #docregion elvis-4 --> <!-- #docregion elvis-4 -->
<!--No hero, div not displayed, no error --> <!--No hero, div not displayed, no error -->
<div *ng-if="nullHero">The null hero's name is {{nullHero.firstName}}</div> <div *ngIf="nullHero">The null hero's name is {{nullHero.firstName}}</div>
<!-- #enddocregion elvis-4 --> <!-- #enddocregion elvis-4 -->
<div> <div>

View File

@ -1,10 +1,10 @@
import {Directive, Output, ElementRef, EventEmitter} from 'angular2/core'; import {Directive, Output, ElementRef, EventEmitter} from 'angular2/core';
@Directive({selector:'[my-click]'}) @Directive({selector:'[mClick]'})
export class MyClickDirective { export class MyClickDirective {
// #docregion my-click-output-1 // #docregion myClick-output-1
@Output('myClick') clicks = new EventEmitter<string>(); @Output('myClick') clicks = new EventEmitter<string>();
// #enddocregion my-click-output-1 // #enddocregion myClick-output-1
constructor(el: ElementRef){ constructor(el: ElementRef){
el.nativeElement el.nativeElement
.addEventListener('click', (event:Event) => { .addEventListener('click', (event:Event) => {
@ -13,14 +13,14 @@ export class MyClickDirective {
} }
} }
// #docregion my-click-output-2 // #docregion myClick-output-2
@Directive({ @Directive({
// #enddocregion my-click-output-2 // #enddocregion myClick-output-2
selector:'[my-click2]', selector:'[myClick2]',
// #docregion my-click-output-2 // #docregion myClick-output-2
outputs:['clicks:myClick'] outputs:['clicks:myClick']
}) })
// #enddocregion my-click-output-2 // #enddocregion myClick-output-2
export class MyClickDirective2 { export class MyClickDirective2 {
clicks = new EventEmitter<string>(); clicks = new EventEmitter<string>();
constructor(el: ElementRef){ constructor(el: ElementRef){

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -24,7 +24,7 @@ include ../../../../_includes/_util-fns
>[Event Binding](#event-binding) >[Event Binding](#event-binding)
>[Two-way data binding with `NgModel`](#ng-model) >[Two-way data binding with `NgModel`](#ngModel)
>[Built-in Directives](#directives) >[Built-in Directives](#directives)
@ -36,14 +36,14 @@ include ../../../../_includes/_util-fns
>[Template Expression Operators](#expression-operators) >[Template Expression Operators](#expression-operators)
[Live Example](/resources/live-examples/template-syntax/ts/src/plnkr.html). [Live Example](/resources/live-examples/template-syntax/ts/plnkr.html).
.l-main-section .l-main-section
:marked :marked
## HTML ## HTML
HTML is the language of the Angular template. Our “[QuickStart](./quickstart.html)” application had a template that was pure HTML HTML is the language of the Angular template. Our “[QuickStart](./quickstart.html)” application had a template that was pure HTML
+makeExample('template-syntax/ts/src/app/app.component.html', 'my-first-app')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'my-first-app')(format=".")
:marked :marked
Almost all HTML syntax is valid template syntax. The `<script>` element is a notable exception; it is forbidden in order to eliminate any possibility of JavaScript injection attacks (in practice it is simply ignored). Almost all HTML syntax is valid template syntax. The `<script>` element is a notable exception; it is forbidden in order to eliminate any possibility of JavaScript injection attacks (in practice it is simply ignored).
@ -57,11 +57,11 @@ include ../../../../_includes/_util-fns
:marked :marked
## Interpolation ## Interpolation
We met the double-curly braces of interpolation, `{{` and `}}`, early in our Angular education. We met the double-curly braces of interpolation, `{{` and `}}`, early in our Angular education.
+makeExample('template-syntax/ts/src/app/app.component.html', 'first-interpolation')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'first-interpolation')(format=".")
:marked :marked
We use interpolation to weave calculated strings into the text between HTML element tags and within attribute assignments. We use interpolation to weave calculated strings into the text between HTML element tags and within attribute assignments.
+makeExample('template-syntax/ts/src/app/app.component.html', 'title+image')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'title+image')(format=".")
:marked :marked
The material between the braces is often the name of a component property. Angular replaces that name with the The material between the braces is often the name of a component property. Angular replaces that name with the
string value of the corresponding component property. In this example, Angular evaluates the `title` and `heroImageUrl` properties string value of the corresponding component property. In this example, Angular evaluates the `title` and `heroImageUrl` properties
@ -69,10 +69,10 @@ include ../../../../_includes/_util-fns
More generally, the material between the braces is a **template expression** that Angular first **evaluates** More generally, the material between the braces is a **template expression** that Angular first **evaluates**
and then **converts to a string**. The following interpolation illustrates the point by adding the two numbers within braces: and then **converts to a string**. The following interpolation illustrates the point by adding the two numbers within braces:
+makeExample('template-syntax/ts/src/app/app.component.html', 'sum-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'sum-1')(format=".")
:marked :marked
The expression can invoke methods of the host component as we do here with `getVal()`: The expression can invoke methods of the host component as we do here with `getVal()`:
+makeExample('template-syntax/ts/src/app/app.component.html', 'sum-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'sum-2')(format=".")
:marked :marked
Angular evaluates all expressions in double curly braces, converts the expression results to strings, and concatenates them with neighboring literal strings. Finally, Angular evaluates all expressions in double curly braces, converts the expression results to strings, and concatenates them with neighboring literal strings. Finally,
it assigns this composite interpolated result to an **element or directive property**. it assigns this composite interpolated result to an **element or directive property**.
@ -194,19 +194,19 @@ table
In the normal course of HTML development, we create a visual structure with HTML elements and In the normal course of HTML development, we create a visual structure with HTML elements and
we modify those elements by setting element attributes with string constants. we modify those elements by setting element attributes with string constants.
+makeExample('template-syntax/ts/src/app/app.component.html', 'img+button')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'img+button')(format=".")
:marked :marked
We still create a structure and initialize attribute values this way in Angular templates. We still create a structure and initialize attribute values this way in Angular templates.
Then we learn to create new elements with Components that encapsulate HTML Then we learn to create new elements with Components that encapsulate HTML
and drop them into our templates as if they were native HTML elements and drop them into our templates as if they were native HTML elements
+makeExample('template-syntax/ts/src/app/app.component.html', 'hero-detail-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'hero-detail-1')(format=".")
:marked :marked
Thats “HTML Plus”. Thats “HTML Plus”.
Now we start to learn about data binding. The first binding we meet might look like this: Now we start to learn about data binding. The first binding we meet might look like this:
+makeExample('template-syntax/ts/src/app/app.component.html', 'disabled-button-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'disabled-button-1')(format=".")
:marked :marked
Well get to that peculiar bracket notation in a moment. Looking beyond it, Well get to that peculiar bracket notation in a moment. Looking beyond it,
our intuition tells us that were binding to the button's `disabled` attribute and setting our intuition tells us that were binding to the button's `disabled` attribute and setting
@ -293,7 +293,7 @@ table
Component&nbsp;Property<br> Component&nbsp;Property<br>
Directive&nbsp;property Directive&nbsp;property
td td
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-syntax-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-syntax-1')(format=".")
tr tr
td Event td Event
td. td.
@ -301,61 +301,34 @@ table
Component&nbsp;Event<br> Component&nbsp;Event<br>
Directive&nbsp;Event Directive&nbsp;Event
td td
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-syntax-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-syntax-1')(format=".")
tr tr
td Two-way td Two-way
td. td.
Directive&nbsp;Event Directive&nbsp;Event
Property Property
td td
+makeExample('template-syntax/ts/src/app/app.component.html', '2-way-binding-syntax-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', '2-way-binding-syntax-1')(format=".")
tr tr
td Attribute td Attribute
td. td.
Attribute Attribute
(the&nbsp;exception) (the&nbsp;exception)
td td
+makeExample('template-syntax/ts/src/app/app.component.html', 'attribute-binding-syntax-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'attribute-binding-syntax-1')(format=".")
tr tr
td Class td Class
td. td.
<code>class</code> Property <code>class</code> Property
td td
+makeExample('template-syntax/ts/src/app/app.component.html', 'class-binding-syntax-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'class-binding-syntax-1')(format=".")
tr tr
td Style td Style
td. td.
<code>style</code> Property <code>style</code> Property
td td
+makeExample('template-syntax/ts/src/app/app.component.html', 'style-binding-syntax-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'style-binding-syntax-1')(format=".")
</div> </div>
:marked
### Spelling target names
.alert.is-critical.
These are current rules. New rules will apply as of alpha 49
:marked
The target name whether between punctuation or after a prefix -
should be spelled with **lowercase**. Although we can spell it in mixed-case,
Angular sees it only after it has been forced to lowercase.
Thats a problem when we need to reference a property with a mixed-case name.
HTML elements have many mixed-case properties: `innerHTML`, `textContent`, `className` to name a few.
Our custom elements (e.g., `<hero-detail>`) may have mixed-case property names
(`HeroDetailComponent.isActive`).
Well be tempted to write bindings that target such properties in mixed-case:
+makeExample('template-syntax/ts/src/app/app.component.html', 'bad-mixed-case')(format=".")
:marked
Try it and the entire display goes blank. A debugger reveals the error:
code-example(format="", language="html").
Template parse errors:
Can't bind to 'textcontent' since it isn't a known native property in ...
:marked
Notice that it forced mixed-case "text**C**ontent" to lowercase "text**c**ontent".
The solution is to write the target property name in **lower kebab-case** (aka *dash-case*),
all lowercase with a dash before each former uppercase letter. We can correct the example as follows:
+makeExample('template-syntax/ts/src/app/app.component.html', 'kebab-case')(format=".")
.l-sub-section .l-sub-section
:marked :marked
@ -371,34 +344,34 @@ code-example(format="", language="html").
The most common Property Binding sets an element property to a component property value as when The most common Property Binding sets an element property to a component property value as when
we bind the source property of an image element to the components `heroImageUrl` property. we bind the source property of an image element to the components `heroImageUrl` property.
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-1')(format=".")
:marked :marked
… or disable a button when the component says that it `isUnchanged`. … or disable a button when the component says that it `isUnchanged`.
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-2')(format=".")
:marked :marked
… or set a property of a directive … or set a property of a directive
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-3')(format=".")
:marked :marked
… or set the model property of a custom component (a great way … or set the model property of a custom component (a great way
for parent and child components to communicate) for parent and child components to communicate)
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-4')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-4')(format=".")
:marked :marked
People often describe Property Binding as “one way data binding” because it can flow a value in one direction, from a components data property to an element property. People often describe Property Binding as “one way data binding” because it can flow a value in one direction, from a components data property to an element property.
### Binding Target ### Binding Target
A name between enclosing square brackets identifies the target property. The target property in this example is the image elements `src` property. A name between enclosing square brackets identifies the target property. The target property in this example is the image elements `src` property.
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-1')(format=".")
:marked :marked
Some developers prefer the `bind-` prefix alternative, known as the “canonical form”: Some developers prefer the `bind-` prefix alternative, known as the “canonical form”:
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-5')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-5')(format=".")
:marked :marked
The target name is always the name of a property, even when it appears to be the name of something else. We see `src` and may think its the name of an attribute. No. Its the name of an image element property. The target name is always the name of a property, even when it appears to be the name of something else. We see `src` and may think its the name of an attribute. No. Its the name of an image element property.
Element properties may be the more common targets, Element properties may be the more common targets,
but Angular looks first to see if the name is a property of a known directive but Angular looks first to see if the name is a property of a known directive
as it is in the following example: as it is in the following example:
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-3')(format=".")
.l-sub-section .l-sub-section
:marked :marked
@ -419,11 +392,11 @@ code-example(format="", language="html").
The template expression should evaluate to a value of the type expected by the target property. Most native element properties do expect a string. The image `src` should be set to a string, an URL for the resource providing the image. On the other hand, the `disabled` property of a button expects a Boolean value so our expression should evaluate to `true` or `false`. The template expression should evaluate to a value of the type expected by the target property. Most native element properties do expect a string. The image `src` should be set to a string, an URL for the resource providing the image. On the other hand, the `disabled` property of a button expects a Boolean value so our expression should evaluate to `true` or `false`.
The `hero` property of the `HeroDetail` component expects a `Hero` object which is exactly what were sending in the Property Binding: The `hero` property of the `HeroDetail` component expects a `Hero` object which is exactly what were sending in the Property Binding:
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-4')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-4')(format=".")
:marked :marked
This is good news for the Angular developer. This is good news for the Angular developer.
If `hero` were an attribute we could not set it to a `Hero` object. If `hero` were an attribute we could not set it to a `Hero` object.
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-6')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-6')(format=".")
:marked :marked
We can't set an attribute to an object. We can only set it to a string. We can't set an attribute to an object. We can only set it to a string.
Internally, the attribute may be able to convert that string to an object before setting the like-named element property. Internally, the attribute may be able to convert that string to an object before setting the like-named element property.
@ -435,7 +408,7 @@ code-example(format="", language="html").
### Property Binding or Interpolation? ### Property Binding or Interpolation?
We often have a choice between Interpolation and Property Binding. The following binding pairs do the same thing We often have a choice between Interpolation and Property Binding. The following binding pairs do the same thing
+makeExample('template-syntax/ts/src/app/app.component.html', 'property-binding-v-interpolation')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation')(format=".")
:marked :marked
Interpolation is actually a convenient alternative for Property Binding in many cases. Interpolation is actually a convenient alternative for Property Binding in many cases.
In fact, Angular translates those Interpolations into the corresponding Property Bindings In fact, Angular translates those Interpolations into the corresponding Property Bindings
@ -489,7 +462,7 @@ code-example(format="", language="html").
followed by the name of the attribute and then set it with an expression that resolves to a string. followed by the name of the attribute and then set it with an expression that resolves to a string.
Here we bind `[attr.colspan]` to a calulated value: Here we bind `[attr.colspan]` to a calulated value:
+makeExample('template-syntax/ts/src/app/app.component.html', 'attrib-binding-colspan')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-colspan')(format=".")
:marked :marked
Here's how the table renders: Here's how the table renders:
<table border="1px"> <table border="1px">
@ -499,7 +472,7 @@ code-example(format="", language="html").
One of the primary use cases for the Attribute Binding One of the primary use cases for the Attribute Binding
is to set aria attributes as we see in this example is to set aria attributes as we see in this example
+makeExample('template-syntax/ts/src/app/app.component.html', 'attrib-binding-aria')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-aria')(format=".")
:marked :marked
### Class Binding ### Class Binding
@ -511,20 +484,20 @@ code-example(format="", language="html").
In the following examples we see how to add and remove the application's "special" class In the following examples we see how to add and remove the application's "special" class
with class bindings. We start by setting the attribute without binding: with class bindings. We start by setting the attribute without binding:
+makeExample('template-syntax/ts/src/app/app.component.html', 'class-binding-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'class-binding-1')(format=".")
:marked :marked
We replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding. We replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding.
+makeExample('template-syntax/ts/src/app/app.component.html', 'class-binding-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'class-binding-2')(format=".")
:marked :marked
Finally, we bind to a specific class name. Finally, we bind to a specific class name.
Angular adds the class when the template expression evaluates to something truthy and Angular adds the class when the template expression evaluates to something truthy and
removes the class name when the expression is falsey. removes the class name when the expression is falsey.
+makeExample('template-syntax/ts/src/app/app.component.html', 'class-binding-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'class-binding-3')(format=".")
.l-sub-section .l-sub-section
:marked :marked
While this is a fine way to toggle a single class name, While this is a fine way to toggle a single class name,
we generally prefer the [NgClass directive](#ng-class) for managing multiple class names at the same time. we generally prefer the [NgClass directive](#ngClass) for managing multiple class names at the same time.
:marked :marked
### Style Binding ### Style Binding
@ -535,15 +508,15 @@ code-example(format="", language="html").
Instead of an element property between brackets, we start with the key word `style` Instead of an element property between brackets, we start with the key word `style`
followed by the name of a CSS style property: `[style.style-property]`. followed by the name of a CSS style property: `[style.style-property]`.
+makeExample('template-syntax/ts/src/app/app.component.html', 'style-binding-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'style-binding-1')(format=".")
:marked :marked
Some Style Binding styles have unit extension. Here we conditionally set the `font-size` in “em” and “%” units . Some Style Binding styles have unit extension. Here we conditionally set the `fontSize` in “em” and “%” units .
+makeExample('template-syntax/ts/src/app/app.component.html', 'style-binding-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'style-binding-2')(format=".")
.l-sub-section .l-sub-section
:marked :marked
While this is a fine way to set a single style, While this is a fine way to set a single style,
we generally prefer the [NgStyle directive](#ng-style) when setting several inline styles at the same time. we generally prefer the [NgStyle directive](#ngStyle) when setting several inline styles at the same time.
.l-main-section .l-main-section
:marked :marked
@ -559,20 +532,20 @@ code-example(format="", language="html").
We declare our interest in user actions through Angular Event Binding. We declare our interest in user actions through Angular Event Binding.
The following Event Binding listens for the buttons click event and calls the component's `onSave()` method: The following Event Binding listens for the buttons click event and calls the component's `onSave()` method:
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".")
:marked :marked
Event Binding syntax consists of a target event on the left of an equal sign and a template expression on the right: `(click)="onSave()"` Event Binding syntax consists of a target event on the left of an equal sign and a template expression on the right: `(click)="onSave()"`
### Binding target ### Binding target
A **name between enclosing parentheses** identifies the target event. In the following example, the target is the buttons click event. A **name between enclosing parentheses** identifies the target event. In the following example, the target is the buttons click event.
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".")
:marked :marked
Some developers prefer the `on-` prefix alternative, known as the “canonical form”: Some developers prefer the `on-` prefix alternative, known as the “canonical form”:
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-2')(format=".")
:marked :marked
Element events may be the more common targets, but Angular looks first to see if the name matches an event property Element events may be the more common targets, but Angular looks first to see if the name matches an event property
of a known directive as it does in the following example: of a known directive as it does in the following example:
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-3')(format=".")
:marked :marked
If the name fails to match an element event or an output property of a known directive, If the name fails to match an element event or an output property of a known directive,
Angular reports an “unknown directive” error. Angular reports an “unknown directive” error.
@ -593,7 +566,7 @@ code-example(format="", language="html").
[DOM event object]( https://developer.mozilla.org/en-US/docs/Web/Events) with properties such as `target` and `target.value`. [DOM event object]( https://developer.mozilla.org/en-US/docs/Web/Events) with properties such as `target` and `target.value`.
Consider this example: Consider this example:
+makeExample('template-syntax/ts/src/app/app.component.html', 'without-NgModel')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".")
:marked :marked
Were binding the input box `value` to a `firstName` property and were listening for changes by binding to the input boxs `input` event. Were binding the input box `value` to a `firstName` property and were listening for changes by binding to the input boxs `input` event.
When the user makes changes, the `input` event is raised, and the binding executes the expression within a context that includes the DOM event object, `$event`. When the user makes changes, the `input` event is raised, and the binding executes the expression within a context that includes the DOM event object, `$event`.
@ -602,12 +575,12 @@ code-example(format="", language="html").
If the event belongs to a directive (remember: components are directives), `$event` has whatever shape the directive chose to produce. If the event belongs to a directive (remember: components are directives), `$event` has whatever shape the directive chose to produce.
Consider a `HeroDetailComponent` that produces `deleted` events with an `EventEmitter`. Consider a `HeroDetailComponent` that produces `deleted` events with an `EventEmitter`.
+makeExample('template-syntax/ts/src/app/hero-detail.component.ts', 'deleted', 'HeroDetailComponent.ts (excerpt)')(format=".") +makeExample('template-syntax/ts/app/hero-detail.component.ts', 'deleted', 'HeroDetailComponent.ts (excerpt)')(format=".")
:marked :marked
When something invokes the `onDeleted()` method, we "emit" a `Hero` object. When something invokes the `onDeleted()` method, we "emit" a `Hero` object.
Now imagine a parent component that listens for that event with an Event Binding. Now imagine a parent component that listens for that event with an Event Binding.
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-to-component')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-to-component')(format=".")
:marked :marked
The event binding surfaces the *hero-to-delete* emitted by `HeroDetail` to the expression via the `$hero` variable. The event binding surfaces the *hero-to-delete* emitted by `HeroDetail` to the expression via the `$hero` variable.
We pass it along to the parent `onHeroDeleted()` method. We pass it along to the parent `onHeroDeleted()` method.
@ -622,7 +595,7 @@ code-example(format="", language="html").
### Event bubbling and propagation ### Event bubbling and propagation
Angular invokes the event-handling expression if the event is raised by the current element or one of its child elements. Angular invokes the event-handling expression if the event is raised by the current element or one of its child elements.
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-bubbling')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-bubbling')(format=".")
:marked :marked
Many DOM events, both [native](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Overview_of_Events_and_Handlers ) and [custom](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events ), “bubble” events up their ancestor tree of DOM elements until an event handler along the way prevents further propagation. Many DOM events, both [native](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Overview_of_Events_and_Handlers ) and [custom](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events ), “bubble” events up their ancestor tree of DOM elements until an event handler along the way prevents further propagation.
@ -638,65 +611,65 @@ code-example(format="", language="html").
Event propagation stops if the binding expression returns a falsey value (as does a method with no return value). Event propagation stops if the binding expression returns a falsey value (as does a method with no return value).
Clicking the button in this next example triggers a save; Clicking the button in this next example triggers a save;
the click doesn't make it to the outer `<div>` so it's save is not called: the click doesn't make it to the outer `<div>` so it's save is not called:
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-no-propagation')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-no-propagation')(format=".")
:marked :marked
Propagation continues if the expression returns a truthy value. The click is heard both by the button Propagation continues if the expression returns a truthy value. The click is heard both by the button
and the outer `<div>`, causing a double save: and the outer `<div>`, causing a double save:
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-propagation')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-propagation')(format=".")
.l-main-section .l-main-section
:marked :marked
<a name="ng-model"></a> <a name="ngModel"></a>
## Two-Way Binding with NgModel ## Two-Way Binding with NgModel
When developing data entry forms we often want to both display a data property and update that property when the user makes changes. When developing data entry forms we often want to both display a data property and update that property when the user makes changes.
The `NgModel` directive serves that purpose as seen in this example: The `NgModel` directive serves that purpose as seen in this example:
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgModel-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".")
:marked :marked
If we prefer the “canonical” prefix form to the punctuation form, we can write If we prefer the “canonical” prefix form to the punctuation form, we can write
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgModel-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-2')(format=".")
:marked :marked
Theres a story behind this construction, a story that builds on the Property and Event Binding techniques we learned previously. Theres a story behind this construction, a story that builds on the Property and Event Binding techniques we learned previously.
We could have achieved the same result as `NgModel` with separate bindings to the We could have achieved the same result as `NgModel` with separate bindings to the
the `<input>` element's `value` property and `input` event. the `<input>` element's `value` property and `input` event.
+makeExample('template-syntax/ts/src/app/app.component.html', 'without-NgModel')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".")
:marked :marked
Thats cumbersome. Who can remember what element property to set and what event reports user changes? Thats cumbersome. Who can remember what element property to set and what event reports user changes?
How do we extract the currently displayed text from the input box so we can update the data property? How do we extract the currently displayed text from the input box so we can update the data property?
Who wants to look that up each time? Who wants to look that up each time?
That `ng-model` directive hides these onerous details. It wraps the elements `value` property, listens to the `input` event, That `ngModel` directive hides these onerous details. It wraps the elements `value` property, listens to the `input` event,
and raises its own event with the changed text ready for us to consume. and raises its own event with the changed text ready for us to consume.
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgModel-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-3')(format=".")
:marked :marked
Thats an improvement. It should be better. Thats an improvement. It should be better.
We shouldn't have to mention the data property twice. Angular should be able to read the components data property and set it We shouldn't have to mention the data property twice. Angular should be able to read the components data property and set it
with a single declaration &mdash; which it can with the `[{ }]` syntax: with a single declaration &mdash; which it can with the `[{ }]` syntax:
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgModel-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".")
.l-sub-section .l-sub-section
:marked :marked
Internally, Angular maps the term, `ng-model`, to an `ng-model` input property and an Internally, Angular maps the term, `ngModel`, to an `ngModel` input property and an
`ng-model-change` output property. `ngModelChange` output property.
Thats a specific example of a more general pattern in which it matches `[(x)]` to an `x` input property Thats a specific example of a more general pattern in which it matches `[(x)]` to an `x` input property
for Property Binding and an `x-change` output property for Event Binding. for Property Binding and an `x-change` output property for Event Binding.
We can write our own two-way binding directive that follows this pattern if we're ever in the mood to do so. We can write our own two-way binding directive that follows this pattern if we're ever in the mood to do so.
:marked :marked
Is `[{ng-model}]` all we need? Is there ever a reason to fall back to its expanded form? Is `[{ngModel}]` all we need? Is there ever a reason to fall back to its expanded form?
Well `NgModel` can only set the target property. Well `NgModel` can only set the target property.
What if we need to do something more or something different when the user changes the value? What if we need to do something more or something different when the user changes the value?
Let's try something silly like forcing the input value to uppercase. Let's try something silly like forcing the input value to uppercase.
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgModel-4')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-4')(format=".")
:marked :marked
Here are all variations in action, including the uppercase version: Here are all variations in action, including the uppercase version:
figure.image-display figure.image-display
img(src='/resources/images/devguide/template-syntax/ng-model-anim.gif' alt="NgModel variations") img(src='/resources/images/devguide/template-syntax/ngModel-anim.gif' alt="NgModel variations")
:marked :marked
.l-main-section .l-main-section
@ -710,7 +683,7 @@ figure.image-display
We dont need many of those directives in Angular 2. We dont need many of those directives in Angular 2.
Quite often we can achieve the same results with the more capable and expressive Angular 2 binding system. Quite often we can achieve the same results with the more capable and expressive Angular 2 binding system.
Why create a directive to handle a click when we can write a simple binding such as this? Why create a directive to handle a click when we can write a simple binding such as this?
+makeExample('template-syntax/ts/src/app/app.component.html', 'event-binding-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-2')(format=".")
:marked :marked
We still benefit from directives that simplify complex tasks. We still benefit from directives that simplify complex tasks.
Angular still ships with built-in directives; just not as many. Angular still ships with built-in directives; just not as many.
@ -718,7 +691,7 @@ figure.image-display
In this segment we review some of the most frequently used built-in directivest. In this segment we review some of the most frequently used built-in directivest.
<a id="ng-class"></a> <a id="ngClass"></a>
.l-main-section .l-main-section
:marked :marked
### NgClass ### NgClass
@ -728,7 +701,7 @@ figure.image-display
We can bind to `NgClass` to add or remove several classes simultaneously. We can bind to `NgClass` to add or remove several classes simultaneously.
We prefer to use a [Class Binding](#class-binding) to add or remove a *single* class. We prefer to use a [Class Binding](#class-binding) to add or remove a *single* class.
+makeExample('template-syntax/ts/src/app/app.component.html', 'class-binding-3a')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'class-binding-3a')(format=".")
:marked :marked
The `NgClass` directive may be the better choice The `NgClass` directive may be the better choice
when we want to add or remove *many* classes at the same time. when we want to add or remove *many* classes at the same time.
@ -736,12 +709,12 @@ figure.image-display
Our favorite way to apply `NgClass` is by binding it to a key:value control object. Each key of the object is a class name and its value is `true` if the class should be added, `false` if it should be removed. Our favorite way to apply `NgClass` is by binding it to a key:value control object. Each key of the object is a class name and its value is `true` if the class should be added, `false` if it should be removed.
Consider a component method such as `setClasses` that returns its approval of two class names: Consider a component method such as `setClasses` that returns its approval of two class names:
+makeExample('template-syntax/ts/src/app/app.component.ts', 'setClasses')(format=".") +makeExample('template-syntax/ts/app/app.component.ts', 'setClasses')(format=".")
:marked :marked
Now add an `NgClass` property binding to call it like this Now add an `NgClass` property binding to call it like this
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgClass-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgClass-1')(format=".")
<a id="ng-style"></a> <a id="ngStyle"></a>
.l-main-section .l-main-section
:marked :marked
### NgStyle ### NgStyle
@ -749,7 +722,7 @@ figure.image-display
We bind to `NgStyle` to set many inline styles simultaneously. We bind to `NgStyle` to set many inline styles simultaneously.
We prefer to use a [Style Binding](#style-binding) to set a *single* style value. We prefer to use a [Style Binding](#style-binding) to set a *single* style value.
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgStyle-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgStyle-1')(format=".")
:marked :marked
The `NgStyle` directive may be the better choice The `NgStyle` directive may be the better choice
when we want to set *many* inline styles at the same time. when we want to set *many* inline styles at the same time.
@ -758,29 +731,29 @@ figure.image-display
Each key of the object is a style name and its value is whatever is appropriate for that style. Each key of the object is a style name and its value is whatever is appropriate for that style.
Consider a component method such as `setStyles` that returns an object defining three styles: Consider a component method such as `setStyles` that returns an object defining three styles:
+makeExample('template-syntax/ts/src/app/app.component.ts', 'setStyles')(format=".") +makeExample('template-syntax/ts/app/app.component.ts', 'setStyles')(format=".")
:marked :marked
Now add an `NgStyle` property binding to call it like this Now add an `NgStyle` property binding to call it like this
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgStyle-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgStyle-2')(format=".")
<a id="ng-if"></a> <a id="ngIf"></a>
.l-main-section .l-main-section
:marked :marked
### NgIf ### NgIf
We can add an element sub-tree (an element and its children) to the DOM by binding an `NgIf` directive to a truthy expression. We can add an element sub-tree (an element and its children) to the DOM by binding an `NgIf` directive to a truthy expression.
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgIf-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgIf-1')(format=".")
.alert.is-critical .alert.is-critical
:marked :marked
The leading asterisk (\*) in front of `ng-if` is a critical part of this syntax. The leading asterisk (\*) in front of `ngIf` is a critical part of this syntax.
See the section below on [\* and &lt;template>](#star-template). See the section below on [\* and &lt;template>](#star-template).
:marked :marked
Binding to a falsey expression removes the element sub-tree from the DOM. Binding to a falsey expression removes the element sub-tree from the DOM.
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgIf-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgIf-2')(format=".")
:marked :marked
#### Visibility and NgIf are not the same #### Visibility and NgIf are not the same
We can show and hide an element sub-tree (the element and its children) with a [Class](#class-binding) or a [Style](#style-binding) binding: We can show and hide an element sub-tree (the element and its children) with a [Class](#class-binding) or a [Style](#style-binding) binding:
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgIf-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgIf-3')(format=".")
:marked :marked
Hiding a sub-tree is quite different from excluding a sub-tree with `NgIf`. Hiding a sub-tree is quite different from excluding a sub-tree with `NgIf`.
@ -796,7 +769,7 @@ figure.image-display
The show/hide technique is probably fine for small element trees. The show/hide technique is probably fine for small element trees.
We should be wary when hiding large trees; `NgIf` may be the safer choice. Always measure before leaping to conclusions. We should be wary when hiding large trees; `NgIf` may be the safer choice. Always measure before leaping to conclusions.
<a id="ng-switch"></a> <a id="ngSwitch"></a>
.l-main-section .l-main-section
:marked :marked
### NgSwitch ### NgSwitch
@ -805,7 +778,7 @@ figure.image-display
Angular only puts the *selected* element tree into the DOM. Angular only puts the *selected* element tree into the DOM.
Heres an example: Heres an example:
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgSwitch')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch')(format=".")
:marked :marked
We bind the parent `NgSwitch` element 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` element to an expression returning a “switch value”. The value is a string in this example but it can be a value of any type.
@ -816,11 +789,11 @@ figure.image-display
If the templates “match value” equals the “switch value”, Angular adds the templates sub-tree to the DOM. If no template is a match and there is a default template, Angular adds the default templates sub-tree to the DOM. Angular removes and destroys the sub-trees of all other templates. If the templates “match value” equals the “switch value”, Angular adds the templates sub-tree to the DOM. If no template is a match and there is a default template, Angular adds the default templates sub-tree to the DOM. Angular removes and destroys the sub-trees of all other templates.
There are three related directives at work here. There are three related directives at work here.
1. `ng-switch` - bound to an expression that returns the switch value. 1. `ngSwitch` - bound to an expression that returns the switch value.
1. `ng-switch-when` - bound to an expression returning a match value. 1. `ngSwitchWhen` - bound to an expression returning a match value.
1. `ng-switch-default` - a marker attribute on the default template. 1. `ngSwitchDefault` - a marker attribute on the default template.
<a id="ng-for"></a> <a id="ngFor"></a>
.l-main-section .l-main-section
:marked :marked
### NgFor ### NgFor
@ -830,22 +803,22 @@ figure.image-display
We tell Angular to use that block as a template for rendering each item in the list. We tell Angular to use that block as a template for rendering each item in the list.
Here is an example of `NgFor` applied to a simple `<div>`. Here is an example of `NgFor` applied to a simple `<div>`.
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgFor-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgFor-1')(format=".")
:marked :marked
We can apply an `NgFor` to a component element as well as we do in this example: We can apply an `NgFor` to a component element as well as we do in this example:
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgFor-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgFor-2')(format=".")
.alert.is-critical .alert.is-critical
:marked :marked
The leading asterisk (\*) in front of `ng-for` is a critical part of this syntax. The leading asterisk (\*) in front of `ngFor` is a critical part of this syntax.
See the section below on [\* and &lt;template>](#star-template). See the section below on [\* and &lt;template>](#star-template).
:marked :marked
The text assigned to `*ng-for` is the instruction that guides the repeater process. The text assigned to `*ngFor` is the instruction that guides the repeater process.
.l-sub-section .l-sub-section
:marked :marked
#### NgFor Micro-syntax #### NgFor Micro-syntax
The string assigned to `*ng-for` is not a [template expression](#template-expressions). The string assigned to `*ngFor` is not a [template expression](#template-expressions).
Its a little language of its own called a “micro-syntax” that Angular interprets. In this example it means: Its a little language of its own called a “micro-syntax” that Angular interprets. In this example it means:
>*Take each hero in the `heroes` array, store it in the local `hero` variable, and make it available to the templated HTML >*Take each hero in the `heroes` array, store it in the local `hero` variable, and make it available to the templated HTML
@ -854,7 +827,7 @@ figure.image-display
Angular translates this instruction into a new set of elements and bindings. Angular translates this instruction into a new set of elements and bindings.
Well talk about this in the next section about templates. Well talk about this in the next section about templates.
:marked :marked
In our two examples, the `ng-for` directive iterates over the `heroes` array returned by the parent components `heroes` property In our two examples, the `ngFor` directive iterates over the `heroes` array returned by the parent components `heroes` property
and stamps out instances of the element to which it is applied. and stamps out instances of the element to which it is applied.
Angular creates a fresh instance of the template for each hero in the array. Angular creates a fresh instance of the template for each hero in the array.
@ -864,12 +837,12 @@ figure.image-display
or we can pass it in a binding to a component element as we're doing with `hero-detail`. or we can pass it in a binding to a component element as we're doing with `hero-detail`.
#### NgFor with index #### NgFor with index
The `ng-for` directive supports an optional index that increases from 0 to the length of the array for each iteration. The `ngFor` directive supports an optional index that increases from 0 to the length of the array for each iteration.
We can capture that in another local template variable (`i`) and use it in our template too. We can capture that in another local template variable (`i`) and use it in our template too.
This next example stamps out rows that display like "1 - Hercules Son of Zeus": This next example stamps out rows that display like "1 - Hercules Son of Zeus":
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgFor-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgFor-3')(format=".")
:marked :marked
<a name="star-template"></a> <a name="star-template"></a>
@ -877,31 +850,31 @@ figure.image-display
.l-main-section .l-main-section
:marked :marked
## * and &lt;template> ## * and &lt;template>
When we reviewed the `ng-for` and `ng-if` built-in directives we called out an oddity of the syntax, the asterisk (*) that appears before the directive name. 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.
This is a bit of “**syntactic sugar**” that makes it easier to read and write directives that modify HTML layout This 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](#ng-switch) directive we always write the `<template>` tags explicitly as we saw [above](#ng-switch). With the [NgSwitch](#ngSwitch) directive we always write the `<template>` tags explicitly as we saw [above](#ngSwitch).
There isnt much choice; we define a different template for each switch choice There isnt much choice; we define a different template for each switch choice
and let the directive render the template that matches the switch value. and let the directive render the template that matches the switch value.
[NgFor](#ng-for) and [NgIf](#ng-if) only need one template, [NgFor](#ngFor) and [NgIf](#ngIf) only need one template,
the *template-to-repeat* and the *template-to-include* respectively. the *template-to-repeat* and the *template-to-include* respectively.
The (\*) prefix syntax is a convenient way for developers to skip the `<template>` wrapper tags and The (\*) prefix syntax is a convenient way for developers to skip the `<template>` wrapper tags and
focus directly on the HTML element to repeat or include. focus directly on the HTML element to repeat or include.
Angular sees the (*) and expands the HTML into the `<template>` tags for us. Angular sees the (*) and expands the HTML into the `<template>` tags for us.
### Expanding `*ng-if` ### Expanding `*ngIf`
We can do that ourselves if we wish. Instead of writing ... We can do that ourselves if we wish. Instead of writing ...
+makeExample('template-syntax/ts/src/app/app.component.html', 'Template-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'Template-1')(format=".")
:marked :marked
… we can write: … we can write:
+makeExample('template-syntax/ts/src/app/app.component.html', 'Template-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'Template-2')(format=".")
:marked :marked
Notice the (\*) is gone and we have a [Property Binding](#property-binding) to the `ng-if` Notice the (\*) is gone and we have a [Property Binding](#property-binding) to the `ngIf`
directive, applied in this case to the `<template>` rather than directive, applied in this case to the `<template>` rather than
the applications `hero-detail` component. the applications `hero-detail` component.
@ -912,26 +885,26 @@ figure.image-display
.callout.is-critical .callout.is-critical
header Remember the brackets! header Remember the brackets!
:marked :marked
Dont make the mistake of writing `ng-if="currentHero"`! Dont make the mistake of writing `ngIf="currentHero"`!
That syntax assigns the *string* value, "currentHero", to `ng-if`. That syntax assigns the *string* value, "currentHero", to `ngIf`.
In JavaScript a non-empty string is a truthy value so `ng-if` would always be In JavaScript a non-empty string is a truthy value so `ngIf` would always be
`true` and Angular will always display the `hero-detail` `true` and Angular will always display the `hero-detail`
… even when there is no `currentHero`! … even when there is no `currentHero`!
:marked :marked
### Expanding `*ng-for` ### Expanding `*ngFor`
A similar transformation applies to `*ng-for`. We can "de-sugar" the syntax ourselves and go from ... A similar transformation applies to `*ngFor`. We can "de-sugar" the syntax ourselves and go from ...
+makeExample('template-syntax/ts/src/app/app.component.html', 'NgFor-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'NgFor-2')(format=".")
:marked :marked
... to ... to
+makeExample('template-syntax/ts/src/app/app.component.html', 'Template-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'Template-3')(format=".")
:marked :marked
... and from there to ... and from there to
+makeExample('template-syntax/ts/src/app/app.component.html', 'Template-4')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'Template-4')(format=".")
:marked :marked
This is a bit more complex than `NgIf` because a repeater has more moving parts to configure. This 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 the `NgForOf` directive that identifies the list.
It should be clear why we prefer the `*ng-for` syntax to writing out this expanded HTML ourselves. It should be clear why we prefer the `*ngFor` syntax to writing out this expanded HTML ourselves.
<a id="local-vars"></a> <a id="local-vars"></a>
.l-main-section .l-main-section
@ -941,11 +914,11 @@ figure.image-display
A **local template variable** is a vehicle for moving data across element lines. A **local template variable** is a vehicle for moving data across element lines.
We've seen the `#hero` local template variable several times in this chapter, We've seen the `#hero` local template variable several times in this chapter,
most prominently when writing [NgFor](#ng-for) repeaters. most prominently when writing [NgFor](#ngFor) repeaters.
In the [* and &lt;templates>](#star-template) segment we learned how Angular expands In the [* and &lt;templates>](#star-template) segment we learned how Angular expands
an `*ng-for` on a component tag into a `<template>` that wraps the component. an `*ngFor` on a component tag into a `<template>` that wraps the component.
+makeExample('template-syntax/ts/src/app/app.component.html', 'Template-4')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'Template-4')(format=".")
:marked :marked
The (#) prefix character in front of "hero" means that we're defining a `hero` variable. The (#) prefix character in front of "hero" means that we're defining a `hero` variable.
.l-sub-section .l-sub-section
@ -965,7 +938,7 @@ figure.image-display
any of its child elements. any of its child elements.
Here are two other examples of creating and consuming a local template variable: Here are two other examples of creating and consuming a local template variable:
+makeExample('template-syntax/ts/src/app/app.component.html', 'var-phone')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'var-phone')(format=".")
:marked :marked
### How it gets its value ### How it gets its value
The value assigned to the variable depends upon the context. The value assigned to the variable depends upon the context.
@ -985,11 +958,11 @@ figure.image-display
The HTML for a form can be quite involved as we saw in the [Forms](forms.html) chapter. The HTML for a form can be quite involved as we saw in the [Forms](forms.html) chapter.
The following is a *simplified* example &mdash; and it's not simple at all. The following is a *simplified* example &mdash; and it's not simple at all.
+makeExample('template-syntax/ts/src/app/app.component.html', 'var-form') +makeExample('template-syntax/ts/app/app.component.html', 'var-form')
:marked :marked
A local template variable, `theForm`, appears three times in this example, separated A local template variable, `theForm`, appears three times in this example, separated
by a large amount of HTML. by a large amount of HTML.
+makeExample('template-syntax/ts/src/app/app.component.html', 'var-form-a') +makeExample('template-syntax/ts/app/app.component.html', 'var-form-a')
:marked :marked
What is the value of `theForm`? What is the value of `theForm`?
@ -1031,12 +1004,12 @@ figure.image-display
In the following example, `iconUrl` and `onSave` are members of the `AppComponent` In the following example, `iconUrl` and `onSave` are members of the `AppComponent`
referenced within template expressions to the *right* of the (=). referenced within template expressions to the *right* of the (=).
+makeExample('template-syntax/ts/src/app/app.component.html', 'io-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'io-1')(format=".")
:marked :marked
They are *neither inputs nor outputs* of `AppComponent`. They are data sources for their bindings. They are *neither inputs nor outputs* of `AppComponent`. They are data sources for their bindings.
Now look at the `HeroDetailComponent` when it is the **target of a binding**. Now look at the `HeroDetailComponent` when it is the **target of a binding**.
+makeExample('template-syntax/ts/src/app/app.component.html', 'io-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'io-2')(format=".")
:marked :marked
Both `HeroDetail.hero` and `HeroDetail.deleted` are on the **left side** of binding expressions. Both `HeroDetail.hero` and `HeroDetail.deleted` are on the **left side** of binding expressions.
`HeroDetail.hero` is the target of a Property Binding. `HeroDetail.deleted` is the target of an Event Binding. `HeroDetail.hero` is the target of a Property Binding. `HeroDetail.deleted` is the target of an Event Binding.
@ -1050,13 +1023,13 @@ figure.image-display
When we peek inside `HeroDetailComponent` we see that these properties are marked When we peek inside `HeroDetailComponent` we see that these properties are marked
with decorators as input and output properties. with decorators as input and output properties.
+makeExample('template-syntax/ts/src/app/hero-detail.component.ts', 'input-output-1')(format=".") +makeExample('template-syntax/ts/app/hero-detail.component.ts', 'input-output-1')(format=".")
:marked :marked
.l-sub-section .l-sub-section
:marked :marked
Alternatively, we can identify them in the `inputs` and `outputs` arrays Alternatively, we can identify them in the `inputs` and `outputs` arrays
of the directive metadata as seen in this example: of the directive metadata as seen in this example:
+makeExample('template-syntax/ts/src/app/hero-detail.component.ts', 'input-output-2')(format=".") +makeExample('template-syntax/ts/app/hero-detail.component.ts', 'input-output-2')(format=".")
<br> <br>
:marked :marked
We can specify an input/output property with a decorator or in one the metadata arrays &mdash; but not both. We can specify an input/output property with a decorator or in one the metadata arrays &mdash; but not both.
@ -1075,12 +1048,12 @@ figure.image-display
Fortunately, we can alias the internal name to meet the conventional needs of the directive's consumer. Fortunately, we can alias the internal name to meet the conventional needs of the directive's consumer.
We alias in decorator syntax like this: We alias in decorator syntax like this:
+makeExample('template-syntax/ts/src/app/my-click.directive.ts', 'my-click-output-1')(format=".") +makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-1')(format=".")
.l-sub-section .l-sub-section
:marked :marked
The equivalent aliasing with the `outputs` array requires a colon-delimited string with The equivalent aliasing with the `outputs` array requires a colon-delimited string with
the internal property name on the left and the public name on the right: the internal property name on the left and the public name on the right:
+makeExample('template-syntax/ts/src/app/my-click.directive.ts', 'my-click-output-2')(format=".") +makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-2')(format=".")
<a id="expression-operators"></a> <a id="expression-operators"></a>
.l-main-section .l-main-section
@ -1096,30 +1069,30 @@ figure.image-display
Angular [Pipes](./pipes.html) are a good choice for small transformations such as these. Angular [Pipes](./pipes.html) are a good choice for small transformations such as these.
Pipes are simple functions that accept an input value and return a transformed value. Pipes are simple functions that accept an input value and return a transformed value.
They are easy to apply within template expressions using the **pipe operator ( | )**: They are easy to apply within template expressions using the **pipe operator ( | )**:
+makeExample('template-syntax/ts/src/app/app.component.html', 'pipes-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'pipes-1')(format=".")
:marked :marked
The pipe operator passes the result of an expression on the left to a pipe function on the right. The pipe operator passes the result of an expression on the left to a pipe function on the right.
We can chain expressions through multiple pipes We can chain expressions through multiple pipes
+makeExample('template-syntax/ts/src/app/app.component.html', 'pipes-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'pipes-2')(format=".")
:marked :marked
And we can configure them too: And we can configure them too:
+makeExample('template-syntax/ts/src/app/app.component.html', 'pipes-3')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'pipes-3')(format=".")
:marked :marked
The `json` pipe is particular helpful for debugging our bindings: The `json` pipe is particular helpful for debugging our bindings:
+makeExample('template-syntax/ts/src/app/app.component.html', 'pipes-json')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'pipes-json')(format=".")
:marked :marked
<a id="elvis"></a> <a id="elvis"></a>
### The Elvis Operator ( ?. ) and null property paths ### The Elvis Operator ( ?. ) and null property paths
The Angular **“Elvis” operator ( ?. )** is a fluent and convenient way to guard against null and undefined values in property paths. The Angular **“Elvis” operator ( ?. )** is a fluent and convenient way to guard against null and undefined values in property paths.
Here it is, protecting against a view render failure if the `currentHero` is null. Here it is, protecting against a view render failure if the `currentHero` is null.
+makeExample('template-syntax/ts/src/app/app.component.html', 'elvis-2')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'elvis-2')(format=".")
:marked :marked
Lets elaborate on the problem and this particular solution. Lets elaborate on the problem and this particular solution.
What happens when the following data bound `title` property is null? What happens when the following data bound `title` property is null?
+makeExample('template-syntax/ts/src/app/app.component.html', 'elvis-1')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'elvis-1')(format=".")
:marked :marked
The view still renders but the displayed value is blank; we see only "`The title is`" with nothing after it. The view still renders but the displayed value is blank; we see only "`The title is`" with nothing after it.
That is reasonable behavior. At least the app doesn't crash. That is reasonable behavior. At least the app doesn't crash.
@ -1149,12 +1122,12 @@ code-example(format="" language="html").
Unfortunately, our app crashes when the `currentHero` is null. Unfortunately, our app crashes when the `currentHero` is null.
We could code around that problem with [NgIf](#ng-if) We could code around that problem with [NgIf](#ngIf)
+makeExample('template-syntax/ts/src/app/app.component.html', 'elvis-4')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'elvis-4')(format=".")
:marked :marked
Or we could try to chain parts of the property path with `&&`, knowing that the expression bails out Or we could try to chain parts of the property path with `&&`, knowing that the expression bails out
when it encounters the first null. when it encounters the first null.
+makeExample('template-syntax/ts/src/app/app.component.html', 'elvis-5')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'elvis-5')(format=".")
:marked :marked
These approaches have merit but they can be cumbersome, especially if the property path is long. These approaches have merit but they can be cumbersome, especially if the property path is long.
Imagine guarding against a null somewhere in a long property path such as `a.b.c.d`. Imagine guarding against a null somewhere in a long property path such as `a.b.c.d`.
@ -1162,7 +1135,7 @@ code-example(format="" language="html").
The Angular **“Elvis” operator ( ?. )** is a more fluent and convenient way to guard against nulls in property paths. The Angular **“Elvis” operator ( ?. )** is a more fluent and convenient way to guard against nulls in property paths.
The expression bails out when it hits the first null value. The expression bails out when it hits the first null value.
The display is blank but the app keeps rolling and there are no errors. The display is blank but the app keeps rolling and there are no errors.
+makeExample('template-syntax/ts/src/app/app.component.html', 'elvis-6')(format=".") +makeExample('template-syntax/ts/app/app.component.html', 'elvis-6')(format=".")
:marked :marked
It works perfectly with long property paths too: It works perfectly with long property paths too:
``` ```