parent
31051d29f0
commit
7cf097ea68
|
@ -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>
|
|
@ -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){
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
@ -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
|
||||||
That’s “HTML Plus”.
|
That’s “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
|
||||||
We’ll get to that peculiar bracket notation in a moment. Looking beyond it,
|
We’ll get to that peculiar bracket notation in a moment. Looking beyond it,
|
||||||
our intuition tells us that we’re binding to the button's `disabled` attribute and setting
|
our intuition tells us that we’re binding to the button's `disabled` attribute and setting
|
||||||
|
@ -293,7 +293,7 @@ table
|
||||||
Component Property<br>
|
Component Property<br>
|
||||||
Directive property
|
Directive 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 Event<br>
|
Component Event<br>
|
||||||
Directive Event
|
Directive 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 Event
|
Directive 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 exception)
|
(the 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.
|
|
||||||
|
|
||||||
That’s 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`).
|
|
||||||
|
|
||||||
We’ll 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 component’s `heroImageUrl` property.
|
we bind the source property of an image element to the component’s `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 component’s 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 component’s 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 element’s `src` property.
|
A name between enclosing square brackets identifies the target property. The target property in this example is the image element’s `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 it’s the name of an attribute. No. It’s 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 it’s the name of an attribute. No. It’s 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 we’re sending in the Property Binding:
|
The `hero` property of the `HeroDetail` component expects a `Hero` object which is exactly what we’re 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 button’s click event and calls the component's `onSave()` method:
|
The following Event Binding listens for the button’s 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 button’s click event.
|
A **name between enclosing parentheses** identifies the target event. In the following example, the target is the button’s 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
|
||||||
We’re binding the input box `value` to a `firstName` property and we’re listening for changes by binding to the input box’s `input` event.
|
We’re binding the input box `value` to a `firstName` property and we’re listening for changes by binding to the input box’s `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
|
||||||
There’s a story behind this construction, a story that builds on the Property and Event Binding techniques we learned previously.
|
There’s 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
|
||||||
That’s cumbersome. Who can remember what element property to set and what event reports user changes?
|
That’s 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 element’s `value` property, listens to the `input` event,
|
That `ngModel` directive hides these onerous details. It wraps the element’s `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
|
||||||
That’s an improvement. It should be better.
|
That’s an improvement. It should be better.
|
||||||
|
|
||||||
We shouldn't have to mention the data property twice. Angular should be able to read the component’s data property and set it
|
We shouldn't have to mention the data property twice. Angular should be able to read the component’s data property and set it
|
||||||
with a single declaration — which it can with the `[{ }]` syntax:
|
with a single declaration — 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.
|
||||||
That’s a specific example of a more general pattern in which it matches `[(x)]` to an `x` input property
|
That’s 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 don’t need many of those directives in Angular 2.
|
We don’t 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 <template>](#star-template).
|
See the section below on [\* and <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.
|
||||||
|
|
||||||
Here’s an example:
|
Here’s 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 template’s “match value” equals the “switch value”, Angular adds the template’s sub-tree to the DOM. If no template is a match and there is a default template, Angular adds the default template’s sub-tree to the DOM. Angular removes and destroys the sub-tree’s of all other templates.
|
If the template’s “match value” equals the “switch value”, Angular adds the template’s sub-tree to the DOM. If no template is a match and there is a default template, Angular adds the default template’s sub-tree to the DOM. Angular removes and destroys the sub-tree’s 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 <template>](#star-template).
|
See the section below on [\* and <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).
|
||||||
It’s a little language of its own called a “micro-syntax” that Angular interprets. In this example it means:
|
It’s 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.
|
||||||
We’ll talk about this in the next section about templates.
|
We’ll 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 component’s `heroes` property
|
In our two examples, the `ngFor` directive iterates over the `heroes` array returned by the parent component’s `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 <template>
|
## * and <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 isn’t much choice; we define a different template for each switch choice
|
There isn’t 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 application’s `hero-detail` component.
|
the application’s `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
|
||||||
Don’t make the mistake of writing `ng-if="currentHero"`!
|
Don’t 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 <templates>](#star-template) segment we learned how Angular expands
|
In the [* and <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 — and it's not simple at all.
|
The following is a *simplified* example — 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 — but not both.
|
We can specify an input/output property with a decorator or in one the metadata arrays — 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
|
||||||
Let’s elaborate on the problem and this particular solution.
|
Let’s 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:
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in New Issue