テンプレートステートメントは、HTMLでユーザーイベントに応答するために使用できるメソッドまたはプロパティです。 テンプレートステートメントを使用すると、アプリケーションは動的なコンテンツの表示やフォームの送信など、アクションを通じてユーザーとやり取りできます。
次の例では、deleteHero()
というテンプレートステートメントが、=
文字の右側に引用符で囲まれています。例として、(event)="statement"
のように表記されます。
src/app/app.component.html
<h1 id="toc">Template Syntax</h1><a href="#interpolation">Interpolation</a><br><a href="#expression-context">Expression context</a><br><a href="#statement-context">Statement context</a><br><a href="#mental-model">Mental Model</a><br><a href="#buttons">Buttons</a><br><a href="#prop-vs-attrib">Properties vs. Attributes</a><br><br><a href="#property-binding">Property Binding</a><br><div style="margin-left:8px"> <a href="#attribute-binding">Attribute Binding</a><br> <a href="#class-binding">Class Binding</a><br> <a href="#style-binding">Style Binding</a><br></div><br><a href="#event-binding">Event Binding</a><br><a href="#two-way">Two-way Binding</a><br><br><div>Directives</div><div style="margin-left:8px"> <a href="#ngModel">NgModel (two-way) Binding</a><br> <a href="#ngClass">NgClass Binding</a><br> <a href="#ngStyle">NgStyle Binding</a><br> <a href="#ngIf">NgIf</a><br> <a href="#ngFor">NgFor</a><br> <div style="margin-left:8px"> <a href="#ngFor-index">NgFor with index</a><br> <a href="#ngFor-trackBy">NgFor with trackBy</a><br> </div> <a href="#ngSwitch">NgSwitch</a><br></div><br><a href="#ref-vars">Template reference variables</a><br><a href="#inputs-and-outputs">Inputs and outputs</a><br><a href="#pipes">Pipes</a><br><a href="#safe-navigation-operator">Safe navigation operator <em>?.</em></a><br><a href="#non-null-assertion-operator">Non-null assertion operator <em>!.</em></a><br><a href="#enums">Enums</a><br><a href="#svg-templates">SVG Templates</a><br><!-- Interpolation and expressions --><hr><h2 id="interpolation">Interpolation</h2><p>My current hero is {{ currentHero.name }}</p><h3> {{ title }} <img alt="{{ hero.name }}" src="{{ heroImageUrl }}" style="height:30px"></h3><!-- "The sum of 1 + 1 is 2" --><p>The sum of 1 + 1 is {{ 1 + 1 }}</p><!-- "The sum of 1 + 1 is not 4" --><p>The sum of 1 + 1 is not {{ 1 + 1 + getVal() }}</p><a class="to-toc" href="#toc">top</a><hr><h2 id="expression-context">Expression context</h2><p>Component expression context ({{title}}, [hidden]="isUnchanged")</p><div class="context"> {{ title }} <span [hidden]="isUnchanged">changed</span></div><p>Template input variable expression context (let hero)</p><!-- template hides the following; plenty of examples later --><ng-template> @for (hero of heroes; track hero) { <div>{{ hero.name }}</div> }</ng-template><p>Template reference variable expression context (#heroInput)</p><div (keyup)="0" class="context"> Type something: <input #heroInput> {{ heroInput.value }}</div><a class="to-toc" href="#toc">top</a><hr><h2 id="statement-context">Statement context</h2><p>Component statement context ( (click)="onSave() )<div class="context"> <button type="button" (click)="deleteHero()">Delete hero</button></div><p>Template $event statement context</p><div class="context"> <button type="button" (click)="onSave($event)">Save</button></div><p>Template input variable statement context (let hero)</p><!-- template hides the following; plenty of examples later --><div class="context"> @for (hero of heroes; track hero) { <button type="button" (click)="deleteHero(hero)">{{ hero.name }}</button> }</div><p>Template reference variable statement context (#heroForm)</p><div class="context"> <form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form></div><a class="to-toc" href="#toc">top</a><!-- New Mental Model --><hr><h2 id="mental-model">New Mental Model</h2><!--<img src="https://wpclipart.com/dl.php?img=/cartoon/people/hero/hero_silhoutte.svg">--><!-- Public Domain terms of use: https://wpclipart.com/terms.html --><div class="special">Mental Model</div><img [alt]="hero.name" src="assets/images/hero.svg"><button type="button" disabled>Save</button><br><br><div> <!-- Normal HTML --> <div class="special">Mental Model</div> <!-- Wow! A new element! --> <app-hero-detail></app-hero-detail></div><br><br><div> <!-- Bind button disabled state to `isUnchanged` property --> <button type="button" [disabled]="isUnchanged">Save</button></div><br><br><div> <img [alt]="hero.name" [src]="heroImageUrl"> <app-hero-detail [hero]="currentHero"></app-hero-detail> <div [ngClass]="{'special': isSpecial}"></div></div><br><br><button type="button" (click)="onSave()">Save</button><app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail><div (myClick)="clicked=$event" clickable>click me</div>{{ clicked }}<br><br><div> Hero Name: <input [(ngModel)]="name"> {{ name }}</div><br><br><button type="button" [attr.aria-label]="help">help</button><br><br><div [class.special]="isSpecial">Special</div><br><br><button type="button" [style.color]="isSpecial ? 'red' : 'green'">button</button><a class="to-toc" href="#toc">top</a><!-- property vs. attribute --><hr><h2 id="prop-vs-attrib">Property vs. Attribute (img examples)</h2><!-- examine the following <img> tag in the browser tools --><img src="images/ng-logo.svg" [src]="heroImageUrl" [alt]="hero.name"><br><br><img [src]="iconUrl" alt="icon"/><img [attr.src]="villainImageUrl" alt="villain-image"/><a class="to-toc" href="#toc">top</a><!-- buttons --><hr><h2 id="buttons">Buttons</h2><button>Enabled (but does nothing)</button><button type="button" disabled>Disabled</button><button type="button" disabled=false>Still disabled</button><br><br><button type="button" disabled>disabled by attribute</button><button type="button" [disabled]="isUnchanged">disabled by property binding</button><br><br><button type="button" [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button><a class="to-toc" href="#toc">top</a><!-- property binding --><hr><h2 id="property-binding">Property Binding</h2><img [alt]="hero.name" [src]="heroImageUrl"><button type="button" [disabled]="isUnchanged">Cancel is disabled</button><div [ngClass]="classes">[ngClass] binding to the classes property</div><app-hero-detail [hero]="currentHero"></app-hero-detail><!-- ERROR: HeroDetailComponent.hero expects a Hero object, not the string "currentHero" --><!-- <app-hero-detail hero="currentHero"></app-hero-detail> --><app-hero-detail prefix="You are my" [hero]="currentHero"></app-hero-detail><p> <img src="{{ heroImageUrl }}" alt="interpolated image of {{ currentHero.name }}"> is the <em>interpolated</em> image.</p><p> <img [src]="heroImageUrl" [alt]="'property bounded image of ' + currentHero.name"> is the <em>property bound</em> image.</p><p><span>"{{ title }}" is the <em>interpolated</em> title.</span></p><p>"<span [innerHTML]="title"></span>" is the <em>property bound</em> title.</p><!-- Angular generates warnings for these two lines as it sanitizes them WARNING: sanitizing HTML stripped some content (see https://g.co/ng/security#xss). --><p><span>"{{evilTitle}}" is the <em>interpolated</em> evil title.</span></p><p>"<span [innerHTML]="evilTitle"></span>" is the <em>property bound</em> evil title.</p><a class="to-toc" href="#toc">top</a><!-- attribute binding --><hr><h2 id="attribute-binding">Attribute Binding</h2><!-- create and set a colspan attribute --><table border=1> <!-- expression calculates colspan=2 --> <tr><td [attr.colspan]="1 + 1">One-Two</td></tr> <!-- ERROR: There is no `colspan` property to set! <tr><td colspan="{{1 + 1}}">Three-Four</td></tr> --> <tr><td>Five</td><td>Six</td></tr></table><br><!-- create and set an aria attribute for assistive technology --><button type="button" [attr.aria-label]="actionName">{{actionName}} with Aria</button><br><br><!-- The following effects are not discussed in the chapter --><div> <!-- any use of [attr.disabled] creates the disabled attribute --> <button type="button" [attr.disabled]="isUnchanged">Disabled</button> <button type="button" [attr.disabled]="!isUnchanged">Disabled as well</button> <!-- we'd have to remove it with property binding --> <button type="button" disabled [disabled]="false">Enabled (but inert)</button></div><a class="to-toc" href="#toc">top</a><!-- class binding --><hr><h2 id="class-binding">Class Binding</h2><!-- standard class attribute setting --><div class="bad curly special">Bad curly special</div><!-- reset/override all class names with a binding --><div class="bad curly special" [class]="badCurly">Bad curly</div><!-- toggle the "special" class on/off with a property --><div [class.special]="isSpecial">The class binding is special</div><!-- binding to `class.special` trumps the class attribute --><div class="special" [class.special]="!isSpecial">This one is not so special</div><a class="to-toc" href="#toc">top</a><!--style binding --><hr><h2 id="style-binding">Style Binding</h2><button type="button" [style.color]="isSpecial ? 'red': 'green'">Red</button><button type="button" [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button><button type="button" [style.font-size.em]="isSpecial ? 3 : 1" >Big</button><button type="button" [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button><a class="to-toc" href="#toc">top</a><!-- event binding --><hr><h2 id="event-binding">Event Binding</h2><button type="button" (click)="onSave()">Save</button><div><!-- `myClick` is an event on the custom `ClickDirective` --><div (myClick)="clickMessage=$event" clickable>click with myClick</div>{{clickMessage}}</div><!-- binding to a nested component --><app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-hero-detail><br><app-big-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-big-hero-detail><div class="parent-div" (click)="onClickMe($event)" clickable>Click me <div class="child-div">Click me too!</div></div><!-- Will save only once --><div (click)="onSave()" clickable> <button type="button" (click)="onSave($event)">Save, no propagation</button></div><!-- Will save twice --><div (click)="onSave()" clickable> <button type="button" (click)="onSave()">Save w/ propagation</button></div><a class="to-toc" href="#toc">top</a><hr><h2 id="two-way">Two-way Binding</h2><div id="two-way-1"> <app-sizer [(size)]="fontSizePx"></app-sizer> <div [style.font-size.px]="fontSizePx">Resizable Text</div> <label for="font-size">FontSize (px): <input id="font-size" [(ngModel)]="fontSizePx"></label></div><br><div id="two-way-2"> <h3>De-sugared two-way binding</h3> <app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer></div><a class="to-toc" href="#toc">top</a><!-- Two way data binding unwound; passing the changed display value to the event handler via `$event` --><hr><h2 id="ngModel">NgModel (two-way) Binding</h2><h3>Result: {{ currentHero.name }}</h3><input [value]="currentHero.name" (input)="updateCurrentHeroName($event)">without NgModel<br><input [(ngModel)]="currentHero.name">[(ngModel)]<br><input [ngModel]="currentHero.name" (ngModelChange)="currentHero.name=$event">(ngModelChange)="...name=$event"<br><input [ngModel]="currentHero.name" (ngModelChange)="setUppercaseName($event)">(ngModelChange)="setUppercaseName($event)"<a class="to-toc" href="#toc">top</a><!-- NgClass binding --><hr><h2 id="ngClass">NgClass Binding</h2><p>currentClasses is {{ currentClasses | json }}</p><div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div><!-- not used in chapter --><br><label for="canSave">saveable <input type="checkbox" id="canSave" [(ngModel)]="canSave"></label> |<label for="isUnchanged">modified: <input type="checkbox" id="isUnchanged" [value]="!isUnchanged" (change)="isUnchanged=!isUnchanged"></label> |<label for="isSpecial">special: <input type="checkbox" id="isSpecial" [(ngModel)]="isSpecial"></label><button type="button" (click)="setCurrentClasses()">Refresh currentClasses</button><br><br><div [ngClass]="currentClasses"> This div should be {{ canSave ? "": "not"}} saveable, {{ isUnchanged ? "unchanged" : "modified" }} and, {{ isSpecial ? "": "not"}} special after clicking "Refresh".</div><br><br><div [ngClass]="isSpecial ? 'special' : ''">This div is special</div><div class="bad curly special">Bad curly special</div><div [ngClass]="{'bad':false, 'curly':true, 'special':true}">Curly special</div><a class="to-toc" href="#toc">top</a><!-- NgStyle binding --><hr><h2 id="ngStyle">NgStyle Binding</h2><div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" > This div is x-large or smaller.</div><h3>[ngStyle] binding to currentStyles - CSS property names</h3><p>currentStyles is {{currentStyles | json}}</p><div [ngStyle]="currentStyles"> This div is initially italic, normal weight, and extra large (24px).</div><!-- not used in chapter --><br><label for="canSave">italic: <input id="canSave" type="checkbox" [(ngModel)]="canSave"></label> |<label for="isUnchanged">normal: <input id="isUnchanged" type="checkbox" [(ngModel)]="isUnchanged"></label> |<label for="isSpecial">xlarge: <input id="isSpecial" type="checkbox" [(ngModel)]="isSpecial"></label><button type="button" (click)="setCurrentStyles()">Refresh currentStyles</button><br><br><div [ngStyle]="currentStyles"> This div should be {{ canSave ? "italic": "plain"}}, {{ isUnchanged ? "normal weight" : "bold" }} and, {{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".</div><a class="to-toc" href="#toc">top</a><!-- NgIf binding --><hr><h2 id="ngIf">NgIf Binding</h2>@if (isActive) { <app-hero-detail></app-hero-detail>}@if (currentHero) { <div>Hello, {{ currentHero.name }}</div>}@if (nullHero) { <div>Hello, {{ nullHero.name }}</div>}<!-- NgIf binding with template (no *) --><ng-template [ngIf]="currentHero">Add {{currentHero.name}} with template</ng-template><!-- Does not show because isActive is false! --><div>Hero Detail removed from DOM (via template) because isActive is false</div><ng-template [ngIf]="isActive"> <app-hero-detail></app-hero-detail></ng-template><!-- isSpecial is true --><div [class.hidden]="!isSpecial">Show with class</div><div [class.hidden]="isSpecial">Hide with class</div><!-- HeroDetail is in the DOM but hidden --><app-hero-detail [class.hidden]="isSpecial"></app-hero-detail><div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div><div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div><a class="to-toc" href="#toc">top</a><!-- NgFor binding --><hr><h2 id="ngFor">NgFor Binding</h2><div class="box"> @for (hero of heroes; track hero) { <div>{{ hero.name }}</div> }</div><br><div class="box"> <!-- *ngFor w/ hero-detail Component --> @for (hero of heroes; track hero) { <app-hero-detail [hero]="hero"></app-hero-detail> }</div><a class="to-toc" href="#toc">top</a><h4 id="ngFor-index">*ngFor with index</h4><p>with <em>semi-colon</em> separator</p><div class="box"> @for (hero of heroes; track hero; let i = $index) { <div>{{ i + 1 }} - {{ hero.name }}</div> }</div><p>with <em>comma</em> separator</p><div class="box"> <!-- Ex: "1 - Hercules" --> @for (hero of heroes; track hero; let i = $index) { <div>{{ i + 1 }} - {{ hero.name }}</div> }</div><a class="to-toc" href="#toc">top</a><h4 id="ngFor-trackBy">*ngFor trackBy</h4><button type="button" (click)="resetHeroes()">Reset heroes</button><button type="button" (click)="changeIds()">Change ids</button><button type="button" (click)="clearTrackByCounts()">Clear counts</button><p><em>without</em> trackBy</p><div class="box"> @for (hero of heroes; track hero) { <div #noTrackBy>({{ hero.id }}) {{ hero.name }}</div> } @if (heroesNoTrackByCount) { <div id="noTrackByCnt" > Hero DOM elements change #{{ heroesNoTrackByCount }} without trackBy </div> }</div><p>with trackBy</p><div class="box"> @for (hero of heroes; track trackByHeroes($index, hero)) { <div #withTrackBy>({{ hero.id }}) {{ hero.name }}</div> } @if (heroesWithTrackByCount) { <div id="withTrackByCnt"> Hero DOM elements change #{{heroesWithTrackByCount}} with trackBy </div> }</div><br><br><br><p>with trackBy and <em>semi-colon</em> separator</p><div class="box"> @for (hero of heroes; track trackByHeroes($index, hero)) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with trackBy and <em>comma</em> separator</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with trackBy and <em>space</em> separator</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with <em>generic</em> trackById function</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><a class="to-toc" href="#toc">top</a><!-- NgSwitch binding --><hr><h2 id="ngSwitch">NgSwitch Binding</h2><p>Pick your favorite hero</p><div> @for (h of heroes; track h) { <label for="hero-{{ h }}"> <input id="hero-{{ h }}" type="radio" name="heroes" [(ngModel)]="currentHero" [value]="h">{{ h.name }} </label> }</div><div> @switch (currentHero.emotion) { @case ('happy') { <app-happy-hero [hero]="currentHero"></app-happy-hero> } @case ('sad') { <app-sad-hero [hero]="currentHero"></app-sad-hero> } @case ('confused') { <app-confused-hero [hero]="currentHero"></app-confused-hero> } @case ('confused') { <div>Are you as confused as {{ currentHero.name }}?</div> } @default { <app-unknown-hero [hero]="currentHero"></app-unknown-hero> } }</div><a class="to-toc" href="#toc">top</a><!-- template reference variable --><hr><h2 id="ref-vars">Template reference variables</h2><input #phone placeholder="phone number"><!-- lots of other elements --><!-- phone refers to the input element; pass its `value` to an event handler --><button type="button" (click)="callPhone(phone.value)">Call</button><!-- btn refers to the button element; show its disabled state --><button type="button" #btn disabled [innerHTML]="'disabled by attribute: '+btn.disabled"></button><h4>Example Form</h4><app-hero-form [hero]="currentHero"></app-hero-form><a class="to-toc" href="#toc">top</a><!-- inputs and output --><hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2><img alt="icon" [src]="iconUrl"/><button type="button" (click)="onSave()">Save</button><app-hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)"></app-hero-detail><div (myClick)="clickMessage2=$event" clickable>myClick2</div>{{ clickMessage2 }}<a class="to-toc" href="#toc">top</a><!-- Pipes --><hr><h2 id="pipes">Pipes</h2><div>Title through uppercase pipe: {{ title | uppercase }}</div><!-- Pipe chaining: convert title to uppercase, then to lowercase --><div> Title through a pipe chain: {{ title | uppercase | lowercase }}</div><!-- pipe with configuration argument => "February 25, 1970" --><div>Birthdate: {{ currentHero.birthdate | date:'longDate' }}</div><div>{{ currentHero | json }}</div><div>Birthdate: {{ (currentHero.birthdate | date:'longDate') | uppercase }}</div><div> <!-- pipe price to USD and display the $ symbol --> <span>Price: </span>{{ product.price | currency:'USD':'symbol' }}</div><a class="to-toc" href="#toc">top</a><!-- Null values and the safe navigation operator --><hr><h2 id="safe-navigation-operator">Safe navigation operator <em>?.</em></h2><div> The title is {{ title }}</div><div> The current hero's name is {{ currentHero.name }}</div><div> The current hero's name is {{ currentHero.name }}</div><!--The null hero's name is {{ nullHero.name }}See console log: TypeError: Cannot read property 'name' of null in [null]--><!--No hero, div not displayed, no error -->@if (nullHero) { <div>The null hero's name is {{ nullHero.name }}</div>}<div>The null hero's name is {{ nullHero && nullHero.name }}</div><div> <!-- No hero, no problem! --> The null hero's name is {{ nullHero?.name }}</div><a class="to-toc" href="#toc">top</a><!-- non-null assertion operator --><hr><h2 id="non-null-assertion-operator">Non-null assertion operator <em>!.</em></h2><div> <!--No hero, no text --> @if (hero) { <div>The hero's name is {{ hero!.name }}</div> }</div><a class="to-toc" href="#toc">top</a><!-- non-null assertion operator --><hr><h2 id="any-type-cast-function">$any type cast function <em>$any( )</em>.</h2><div> <!-- Accessing an undeclared member --> <div> The hero's marker is {{ $any(hero).marker }} </div></div><div> <!-- Accessing an undeclared member --> <div> Undeclared members is {{ $any(this).member }} </div></div><a class="to-toc" href="#toc">top</a><!-- TODO: discuss this in the Style binding section --><!-- enums in bindings --><hr><h2 id="enums">Enums in binding</h2><p> The name of the Color.Red enum is {{ Color[Color.Red] }}.<br> The current color is {{ Color[color] }} and its number is {{ color }}.<br> <button type="button" [style.color]="Color[color]" (click)="colorToggle()">Enum Toggle</button></p><a class="to-toc" href="#toc">top</a><hr><h2 id="svg-templates">SVG Templates</h2><app-svg></app-svg><a class="to-toc" href="#toc">top</a>
ユーザーが Delete hero ボタンをクリックすると、Angularはコンポーネントクラスの deleteHero()
メソッドを呼び出します。
テンプレートステートメントは、イベントに応答する要素、コンポーネント、またはディレクティブと共に使用します。
HELPFUL: イベントに応答することは、Angularの単方向データフローの一部です。 単一のイベントループ中に、アプリケーション内のあらゆるものを変更できます。
構文
テンプレート式 と同様に、テンプレートステートメントはJavaScriptに似た言語を使用します。
ただし、テンプレートステートメントのパーサーはテンプレート式のパーサーとは異なります。
さらに、テンプレートステートメントのパーサーは、基本的な代入 (=
) と、セミコロン (;
) で区切られたチェーン式の両方を特にサポートしています。
次のJavaScriptとテンプレート式の構文は使用できません。
new
- インクリメント演算子とデクリメント演算子、
++
と--
- 演算子代入、
+=
や-=
など - ビット演算子、
|
や&
など - パイプ演算子
ステートメントコンテキスト
ステートメントには、コンテキスト — ステートメントが属するアプリケーションの特定の部分 — があります。
ステートメントは、ステートメントコンテキストにあるもののみを参照できます。ステートメントコンテキストは通常、コンポーネントインスタンスです。
たとえば、(click)="deleteHero()"
の deleteHero()
は、次のスニペットのコンポーネントのメソッドです。
src/app/app.component.html
<h1 id="toc">Template Syntax</h1><a href="#interpolation">Interpolation</a><br><a href="#expression-context">Expression context</a><br><a href="#statement-context">Statement context</a><br><a href="#mental-model">Mental Model</a><br><a href="#buttons">Buttons</a><br><a href="#prop-vs-attrib">Properties vs. Attributes</a><br><br><a href="#property-binding">Property Binding</a><br><div style="margin-left:8px"> <a href="#attribute-binding">Attribute Binding</a><br> <a href="#class-binding">Class Binding</a><br> <a href="#style-binding">Style Binding</a><br></div><br><a href="#event-binding">Event Binding</a><br><a href="#two-way">Two-way Binding</a><br><br><div>Directives</div><div style="margin-left:8px"> <a href="#ngModel">NgModel (two-way) Binding</a><br> <a href="#ngClass">NgClass Binding</a><br> <a href="#ngStyle">NgStyle Binding</a><br> <a href="#ngIf">NgIf</a><br> <a href="#ngFor">NgFor</a><br> <div style="margin-left:8px"> <a href="#ngFor-index">NgFor with index</a><br> <a href="#ngFor-trackBy">NgFor with trackBy</a><br> </div> <a href="#ngSwitch">NgSwitch</a><br></div><br><a href="#ref-vars">Template reference variables</a><br><a href="#inputs-and-outputs">Inputs and outputs</a><br><a href="#pipes">Pipes</a><br><a href="#safe-navigation-operator">Safe navigation operator <em>?.</em></a><br><a href="#non-null-assertion-operator">Non-null assertion operator <em>!.</em></a><br><a href="#enums">Enums</a><br><a href="#svg-templates">SVG Templates</a><br><!-- Interpolation and expressions --><hr><h2 id="interpolation">Interpolation</h2><p>My current hero is {{ currentHero.name }}</p><h3> {{ title }} <img alt="{{ hero.name }}" src="{{ heroImageUrl }}" style="height:30px"></h3><!-- "The sum of 1 + 1 is 2" --><p>The sum of 1 + 1 is {{ 1 + 1 }}</p><!-- "The sum of 1 + 1 is not 4" --><p>The sum of 1 + 1 is not {{ 1 + 1 + getVal() }}</p><a class="to-toc" href="#toc">top</a><hr><h2 id="expression-context">Expression context</h2><p>Component expression context ({{title}}, [hidden]="isUnchanged")</p><div class="context"> {{ title }} <span [hidden]="isUnchanged">changed</span></div><p>Template input variable expression context (let hero)</p><!-- template hides the following; plenty of examples later --><ng-template> @for (hero of heroes; track hero) { <div>{{ hero.name }}</div> }</ng-template><p>Template reference variable expression context (#heroInput)</p><div (keyup)="0" class="context"> Type something: <input #heroInput> {{ heroInput.value }}</div><a class="to-toc" href="#toc">top</a><hr><h2 id="statement-context">Statement context</h2><p>Component statement context ( (click)="onSave() )<div class="context"> <button type="button" (click)="deleteHero()">Delete hero</button></div><p>Template $event statement context</p><div class="context"> <button type="button" (click)="onSave($event)">Save</button></div><p>Template input variable statement context (let hero)</p><!-- template hides the following; plenty of examples later --><div class="context"> @for (hero of heroes; track hero) { <button type="button" (click)="deleteHero(hero)">{{ hero.name }}</button> }</div><p>Template reference variable statement context (#heroForm)</p><div class="context"> <form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form></div><a class="to-toc" href="#toc">top</a><!-- New Mental Model --><hr><h2 id="mental-model">New Mental Model</h2><!--<img src="https://wpclipart.com/dl.php?img=/cartoon/people/hero/hero_silhoutte.svg">--><!-- Public Domain terms of use: https://wpclipart.com/terms.html --><div class="special">Mental Model</div><img [alt]="hero.name" src="assets/images/hero.svg"><button type="button" disabled>Save</button><br><br><div> <!-- Normal HTML --> <div class="special">Mental Model</div> <!-- Wow! A new element! --> <app-hero-detail></app-hero-detail></div><br><br><div> <!-- Bind button disabled state to `isUnchanged` property --> <button type="button" [disabled]="isUnchanged">Save</button></div><br><br><div> <img [alt]="hero.name" [src]="heroImageUrl"> <app-hero-detail [hero]="currentHero"></app-hero-detail> <div [ngClass]="{'special': isSpecial}"></div></div><br><br><button type="button" (click)="onSave()">Save</button><app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail><div (myClick)="clicked=$event" clickable>click me</div>{{ clicked }}<br><br><div> Hero Name: <input [(ngModel)]="name"> {{ name }}</div><br><br><button type="button" [attr.aria-label]="help">help</button><br><br><div [class.special]="isSpecial">Special</div><br><br><button type="button" [style.color]="isSpecial ? 'red' : 'green'">button</button><a class="to-toc" href="#toc">top</a><!-- property vs. attribute --><hr><h2 id="prop-vs-attrib">Property vs. Attribute (img examples)</h2><!-- examine the following <img> tag in the browser tools --><img src="images/ng-logo.svg" [src]="heroImageUrl" [alt]="hero.name"><br><br><img [src]="iconUrl" alt="icon"/><img [attr.src]="villainImageUrl" alt="villain-image"/><a class="to-toc" href="#toc">top</a><!-- buttons --><hr><h2 id="buttons">Buttons</h2><button>Enabled (but does nothing)</button><button type="button" disabled>Disabled</button><button type="button" disabled=false>Still disabled</button><br><br><button type="button" disabled>disabled by attribute</button><button type="button" [disabled]="isUnchanged">disabled by property binding</button><br><br><button type="button" [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button><a class="to-toc" href="#toc">top</a><!-- property binding --><hr><h2 id="property-binding">Property Binding</h2><img [alt]="hero.name" [src]="heroImageUrl"><button type="button" [disabled]="isUnchanged">Cancel is disabled</button><div [ngClass]="classes">[ngClass] binding to the classes property</div><app-hero-detail [hero]="currentHero"></app-hero-detail><!-- ERROR: HeroDetailComponent.hero expects a Hero object, not the string "currentHero" --><!-- <app-hero-detail hero="currentHero"></app-hero-detail> --><app-hero-detail prefix="You are my" [hero]="currentHero"></app-hero-detail><p> <img src="{{ heroImageUrl }}" alt="interpolated image of {{ currentHero.name }}"> is the <em>interpolated</em> image.</p><p> <img [src]="heroImageUrl" [alt]="'property bounded image of ' + currentHero.name"> is the <em>property bound</em> image.</p><p><span>"{{ title }}" is the <em>interpolated</em> title.</span></p><p>"<span [innerHTML]="title"></span>" is the <em>property bound</em> title.</p><!-- Angular generates warnings for these two lines as it sanitizes them WARNING: sanitizing HTML stripped some content (see https://g.co/ng/security#xss). --><p><span>"{{evilTitle}}" is the <em>interpolated</em> evil title.</span></p><p>"<span [innerHTML]="evilTitle"></span>" is the <em>property bound</em> evil title.</p><a class="to-toc" href="#toc">top</a><!-- attribute binding --><hr><h2 id="attribute-binding">Attribute Binding</h2><!-- create and set a colspan attribute --><table border=1> <!-- expression calculates colspan=2 --> <tr><td [attr.colspan]="1 + 1">One-Two</td></tr> <!-- ERROR: There is no `colspan` property to set! <tr><td colspan="{{1 + 1}}">Three-Four</td></tr> --> <tr><td>Five</td><td>Six</td></tr></table><br><!-- create and set an aria attribute for assistive technology --><button type="button" [attr.aria-label]="actionName">{{actionName}} with Aria</button><br><br><!-- The following effects are not discussed in the chapter --><div> <!-- any use of [attr.disabled] creates the disabled attribute --> <button type="button" [attr.disabled]="isUnchanged">Disabled</button> <button type="button" [attr.disabled]="!isUnchanged">Disabled as well</button> <!-- we'd have to remove it with property binding --> <button type="button" disabled [disabled]="false">Enabled (but inert)</button></div><a class="to-toc" href="#toc">top</a><!-- class binding --><hr><h2 id="class-binding">Class Binding</h2><!-- standard class attribute setting --><div class="bad curly special">Bad curly special</div><!-- reset/override all class names with a binding --><div class="bad curly special" [class]="badCurly">Bad curly</div><!-- toggle the "special" class on/off with a property --><div [class.special]="isSpecial">The class binding is special</div><!-- binding to `class.special` trumps the class attribute --><div class="special" [class.special]="!isSpecial">This one is not so special</div><a class="to-toc" href="#toc">top</a><!--style binding --><hr><h2 id="style-binding">Style Binding</h2><button type="button" [style.color]="isSpecial ? 'red': 'green'">Red</button><button type="button" [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button><button type="button" [style.font-size.em]="isSpecial ? 3 : 1" >Big</button><button type="button" [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button><a class="to-toc" href="#toc">top</a><!-- event binding --><hr><h2 id="event-binding">Event Binding</h2><button type="button" (click)="onSave()">Save</button><div><!-- `myClick` is an event on the custom `ClickDirective` --><div (myClick)="clickMessage=$event" clickable>click with myClick</div>{{clickMessage}}</div><!-- binding to a nested component --><app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-hero-detail><br><app-big-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-big-hero-detail><div class="parent-div" (click)="onClickMe($event)" clickable>Click me <div class="child-div">Click me too!</div></div><!-- Will save only once --><div (click)="onSave()" clickable> <button type="button" (click)="onSave($event)">Save, no propagation</button></div><!-- Will save twice --><div (click)="onSave()" clickable> <button type="button" (click)="onSave()">Save w/ propagation</button></div><a class="to-toc" href="#toc">top</a><hr><h2 id="two-way">Two-way Binding</h2><div id="two-way-1"> <app-sizer [(size)]="fontSizePx"></app-sizer> <div [style.font-size.px]="fontSizePx">Resizable Text</div> <label for="font-size">FontSize (px): <input id="font-size" [(ngModel)]="fontSizePx"></label></div><br><div id="two-way-2"> <h3>De-sugared two-way binding</h3> <app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer></div><a class="to-toc" href="#toc">top</a><!-- Two way data binding unwound; passing the changed display value to the event handler via `$event` --><hr><h2 id="ngModel">NgModel (two-way) Binding</h2><h3>Result: {{ currentHero.name }}</h3><input [value]="currentHero.name" (input)="updateCurrentHeroName($event)">without NgModel<br><input [(ngModel)]="currentHero.name">[(ngModel)]<br><input [ngModel]="currentHero.name" (ngModelChange)="currentHero.name=$event">(ngModelChange)="...name=$event"<br><input [ngModel]="currentHero.name" (ngModelChange)="setUppercaseName($event)">(ngModelChange)="setUppercaseName($event)"<a class="to-toc" href="#toc">top</a><!-- NgClass binding --><hr><h2 id="ngClass">NgClass Binding</h2><p>currentClasses is {{ currentClasses | json }}</p><div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div><!-- not used in chapter --><br><label for="canSave">saveable <input type="checkbox" id="canSave" [(ngModel)]="canSave"></label> |<label for="isUnchanged">modified: <input type="checkbox" id="isUnchanged" [value]="!isUnchanged" (change)="isUnchanged=!isUnchanged"></label> |<label for="isSpecial">special: <input type="checkbox" id="isSpecial" [(ngModel)]="isSpecial"></label><button type="button" (click)="setCurrentClasses()">Refresh currentClasses</button><br><br><div [ngClass]="currentClasses"> This div should be {{ canSave ? "": "not"}} saveable, {{ isUnchanged ? "unchanged" : "modified" }} and, {{ isSpecial ? "": "not"}} special after clicking "Refresh".</div><br><br><div [ngClass]="isSpecial ? 'special' : ''">This div is special</div><div class="bad curly special">Bad curly special</div><div [ngClass]="{'bad':false, 'curly':true, 'special':true}">Curly special</div><a class="to-toc" href="#toc">top</a><!-- NgStyle binding --><hr><h2 id="ngStyle">NgStyle Binding</h2><div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" > This div is x-large or smaller.</div><h3>[ngStyle] binding to currentStyles - CSS property names</h3><p>currentStyles is {{currentStyles | json}}</p><div [ngStyle]="currentStyles"> This div is initially italic, normal weight, and extra large (24px).</div><!-- not used in chapter --><br><label for="canSave">italic: <input id="canSave" type="checkbox" [(ngModel)]="canSave"></label> |<label for="isUnchanged">normal: <input id="isUnchanged" type="checkbox" [(ngModel)]="isUnchanged"></label> |<label for="isSpecial">xlarge: <input id="isSpecial" type="checkbox" [(ngModel)]="isSpecial"></label><button type="button" (click)="setCurrentStyles()">Refresh currentStyles</button><br><br><div [ngStyle]="currentStyles"> This div should be {{ canSave ? "italic": "plain"}}, {{ isUnchanged ? "normal weight" : "bold" }} and, {{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".</div><a class="to-toc" href="#toc">top</a><!-- NgIf binding --><hr><h2 id="ngIf">NgIf Binding</h2>@if (isActive) { <app-hero-detail></app-hero-detail>}@if (currentHero) { <div>Hello, {{ currentHero.name }}</div>}@if (nullHero) { <div>Hello, {{ nullHero.name }}</div>}<!-- NgIf binding with template (no *) --><ng-template [ngIf]="currentHero">Add {{currentHero.name}} with template</ng-template><!-- Does not show because isActive is false! --><div>Hero Detail removed from DOM (via template) because isActive is false</div><ng-template [ngIf]="isActive"> <app-hero-detail></app-hero-detail></ng-template><!-- isSpecial is true --><div [class.hidden]="!isSpecial">Show with class</div><div [class.hidden]="isSpecial">Hide with class</div><!-- HeroDetail is in the DOM but hidden --><app-hero-detail [class.hidden]="isSpecial"></app-hero-detail><div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div><div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div><a class="to-toc" href="#toc">top</a><!-- NgFor binding --><hr><h2 id="ngFor">NgFor Binding</h2><div class="box"> @for (hero of heroes; track hero) { <div>{{ hero.name }}</div> }</div><br><div class="box"> <!-- *ngFor w/ hero-detail Component --> @for (hero of heroes; track hero) { <app-hero-detail [hero]="hero"></app-hero-detail> }</div><a class="to-toc" href="#toc">top</a><h4 id="ngFor-index">*ngFor with index</h4><p>with <em>semi-colon</em> separator</p><div class="box"> @for (hero of heroes; track hero; let i = $index) { <div>{{ i + 1 }} - {{ hero.name }}</div> }</div><p>with <em>comma</em> separator</p><div class="box"> <!-- Ex: "1 - Hercules" --> @for (hero of heroes; track hero; let i = $index) { <div>{{ i + 1 }} - {{ hero.name }}</div> }</div><a class="to-toc" href="#toc">top</a><h4 id="ngFor-trackBy">*ngFor trackBy</h4><button type="button" (click)="resetHeroes()">Reset heroes</button><button type="button" (click)="changeIds()">Change ids</button><button type="button" (click)="clearTrackByCounts()">Clear counts</button><p><em>without</em> trackBy</p><div class="box"> @for (hero of heroes; track hero) { <div #noTrackBy>({{ hero.id }}) {{ hero.name }}</div> } @if (heroesNoTrackByCount) { <div id="noTrackByCnt" > Hero DOM elements change #{{ heroesNoTrackByCount }} without trackBy </div> }</div><p>with trackBy</p><div class="box"> @for (hero of heroes; track trackByHeroes($index, hero)) { <div #withTrackBy>({{ hero.id }}) {{ hero.name }}</div> } @if (heroesWithTrackByCount) { <div id="withTrackByCnt"> Hero DOM elements change #{{heroesWithTrackByCount}} with trackBy </div> }</div><br><br><br><p>with trackBy and <em>semi-colon</em> separator</p><div class="box"> @for (hero of heroes; track trackByHeroes($index, hero)) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with trackBy and <em>comma</em> separator</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with trackBy and <em>space</em> separator</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with <em>generic</em> trackById function</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><a class="to-toc" href="#toc">top</a><!-- NgSwitch binding --><hr><h2 id="ngSwitch">NgSwitch Binding</h2><p>Pick your favorite hero</p><div> @for (h of heroes; track h) { <label for="hero-{{ h }}"> <input id="hero-{{ h }}" type="radio" name="heroes" [(ngModel)]="currentHero" [value]="h">{{ h.name }} </label> }</div><div> @switch (currentHero.emotion) { @case ('happy') { <app-happy-hero [hero]="currentHero"></app-happy-hero> } @case ('sad') { <app-sad-hero [hero]="currentHero"></app-sad-hero> } @case ('confused') { <app-confused-hero [hero]="currentHero"></app-confused-hero> } @case ('confused') { <div>Are you as confused as {{ currentHero.name }}?</div> } @default { <app-unknown-hero [hero]="currentHero"></app-unknown-hero> } }</div><a class="to-toc" href="#toc">top</a><!-- template reference variable --><hr><h2 id="ref-vars">Template reference variables</h2><input #phone placeholder="phone number"><!-- lots of other elements --><!-- phone refers to the input element; pass its `value` to an event handler --><button type="button" (click)="callPhone(phone.value)">Call</button><!-- btn refers to the button element; show its disabled state --><button type="button" #btn disabled [innerHTML]="'disabled by attribute: '+btn.disabled"></button><h4>Example Form</h4><app-hero-form [hero]="currentHero"></app-hero-form><a class="to-toc" href="#toc">top</a><!-- inputs and output --><hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2><img alt="icon" [src]="iconUrl"/><button type="button" (click)="onSave()">Save</button><app-hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)"></app-hero-detail><div (myClick)="clickMessage2=$event" clickable>myClick2</div>{{ clickMessage2 }}<a class="to-toc" href="#toc">top</a><!-- Pipes --><hr><h2 id="pipes">Pipes</h2><div>Title through uppercase pipe: {{ title | uppercase }}</div><!-- Pipe chaining: convert title to uppercase, then to lowercase --><div> Title through a pipe chain: {{ title | uppercase | lowercase }}</div><!-- pipe with configuration argument => "February 25, 1970" --><div>Birthdate: {{ currentHero.birthdate | date:'longDate' }}</div><div>{{ currentHero | json }}</div><div>Birthdate: {{ (currentHero.birthdate | date:'longDate') | uppercase }}</div><div> <!-- pipe price to USD and display the $ symbol --> <span>Price: </span>{{ product.price | currency:'USD':'symbol' }}</div><a class="to-toc" href="#toc">top</a><!-- Null values and the safe navigation operator --><hr><h2 id="safe-navigation-operator">Safe navigation operator <em>?.</em></h2><div> The title is {{ title }}</div><div> The current hero's name is {{ currentHero.name }}</div><div> The current hero's name is {{ currentHero.name }}</div><!--The null hero's name is {{ nullHero.name }}See console log: TypeError: Cannot read property 'name' of null in [null]--><!--No hero, div not displayed, no error -->@if (nullHero) { <div>The null hero's name is {{ nullHero.name }}</div>}<div>The null hero's name is {{ nullHero && nullHero.name }}</div><div> <!-- No hero, no problem! --> The null hero's name is {{ nullHero?.name }}</div><a class="to-toc" href="#toc">top</a><!-- non-null assertion operator --><hr><h2 id="non-null-assertion-operator">Non-null assertion operator <em>!.</em></h2><div> <!--No hero, no text --> @if (hero) { <div>The hero's name is {{ hero!.name }}</div> }</div><a class="to-toc" href="#toc">top</a><!-- non-null assertion operator --><hr><h2 id="any-type-cast-function">$any type cast function <em>$any( )</em>.</h2><div> <!-- Accessing an undeclared member --> <div> The hero's marker is {{ $any(hero).marker }} </div></div><div> <!-- Accessing an undeclared member --> <div> Undeclared members is {{ $any(this).member }} </div></div><a class="to-toc" href="#toc">top</a><!-- TODO: discuss this in the Style binding section --><!-- enums in bindings --><hr><h2 id="enums">Enums in binding</h2><p> The name of the Color.Red enum is {{ Color[Color.Red] }}.<br> The current color is {{ Color[color] }} and its number is {{ color }}.<br> <button type="button" [style.color]="Color[color]" (click)="colorToggle()">Enum Toggle</button></p><a class="to-toc" href="#toc">top</a><hr><h2 id="svg-templates">SVG Templates</h2><app-svg></app-svg><a class="to-toc" href="#toc">top</a>
ステートメントコンテキストは、テンプレート自身のコンテキストのプロパティを参照もできます。
次の例では、コンポーネントのイベント処理メソッド onSave()
は、テンプレート自身の $event
オブジェクトを引数として受け取ります。
次の2行では、deleteHero()
メソッドは テンプレート入力変数 の hero
を受け取り、onSubmit()
は テンプレート参照変数 の #heroForm
を受け取ります。
src/app/app.component.html
<h1 id="toc">Template Syntax</h1><a href="#interpolation">Interpolation</a><br><a href="#expression-context">Expression context</a><br><a href="#statement-context">Statement context</a><br><a href="#mental-model">Mental Model</a><br><a href="#buttons">Buttons</a><br><a href="#prop-vs-attrib">Properties vs. Attributes</a><br><br><a href="#property-binding">Property Binding</a><br><div style="margin-left:8px"> <a href="#attribute-binding">Attribute Binding</a><br> <a href="#class-binding">Class Binding</a><br> <a href="#style-binding">Style Binding</a><br></div><br><a href="#event-binding">Event Binding</a><br><a href="#two-way">Two-way Binding</a><br><br><div>Directives</div><div style="margin-left:8px"> <a href="#ngModel">NgModel (two-way) Binding</a><br> <a href="#ngClass">NgClass Binding</a><br> <a href="#ngStyle">NgStyle Binding</a><br> <a href="#ngIf">NgIf</a><br> <a href="#ngFor">NgFor</a><br> <div style="margin-left:8px"> <a href="#ngFor-index">NgFor with index</a><br> <a href="#ngFor-trackBy">NgFor with trackBy</a><br> </div> <a href="#ngSwitch">NgSwitch</a><br></div><br><a href="#ref-vars">Template reference variables</a><br><a href="#inputs-and-outputs">Inputs and outputs</a><br><a href="#pipes">Pipes</a><br><a href="#safe-navigation-operator">Safe navigation operator <em>?.</em></a><br><a href="#non-null-assertion-operator">Non-null assertion operator <em>!.</em></a><br><a href="#enums">Enums</a><br><a href="#svg-templates">SVG Templates</a><br><!-- Interpolation and expressions --><hr><h2 id="interpolation">Interpolation</h2><p>My current hero is {{ currentHero.name }}</p><h3> {{ title }} <img alt="{{ hero.name }}" src="{{ heroImageUrl }}" style="height:30px"></h3><!-- "The sum of 1 + 1 is 2" --><p>The sum of 1 + 1 is {{ 1 + 1 }}</p><!-- "The sum of 1 + 1 is not 4" --><p>The sum of 1 + 1 is not {{ 1 + 1 + getVal() }}</p><a class="to-toc" href="#toc">top</a><hr><h2 id="expression-context">Expression context</h2><p>Component expression context ({{title}}, [hidden]="isUnchanged")</p><div class="context"> {{ title }} <span [hidden]="isUnchanged">changed</span></div><p>Template input variable expression context (let hero)</p><!-- template hides the following; plenty of examples later --><ng-template> @for (hero of heroes; track hero) { <div>{{ hero.name }}</div> }</ng-template><p>Template reference variable expression context (#heroInput)</p><div (keyup)="0" class="context"> Type something: <input #heroInput> {{ heroInput.value }}</div><a class="to-toc" href="#toc">top</a><hr><h2 id="statement-context">Statement context</h2><p>Component statement context ( (click)="onSave() )<div class="context"> <button type="button" (click)="deleteHero()">Delete hero</button></div><p>Template $event statement context</p><div class="context"> <button type="button" (click)="onSave($event)">Save</button></div><p>Template input variable statement context (let hero)</p><!-- template hides the following; plenty of examples later --><div class="context"> @for (hero of heroes; track hero) { <button type="button" (click)="deleteHero(hero)">{{ hero.name }}</button> }</div><p>Template reference variable statement context (#heroForm)</p><div class="context"> <form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form></div><a class="to-toc" href="#toc">top</a><!-- New Mental Model --><hr><h2 id="mental-model">New Mental Model</h2><!--<img src="https://wpclipart.com/dl.php?img=/cartoon/people/hero/hero_silhoutte.svg">--><!-- Public Domain terms of use: https://wpclipart.com/terms.html --><div class="special">Mental Model</div><img [alt]="hero.name" src="assets/images/hero.svg"><button type="button" disabled>Save</button><br><br><div> <!-- Normal HTML --> <div class="special">Mental Model</div> <!-- Wow! A new element! --> <app-hero-detail></app-hero-detail></div><br><br><div> <!-- Bind button disabled state to `isUnchanged` property --> <button type="button" [disabled]="isUnchanged">Save</button></div><br><br><div> <img [alt]="hero.name" [src]="heroImageUrl"> <app-hero-detail [hero]="currentHero"></app-hero-detail> <div [ngClass]="{'special': isSpecial}"></div></div><br><br><button type="button" (click)="onSave()">Save</button><app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail><div (myClick)="clicked=$event" clickable>click me</div>{{ clicked }}<br><br><div> Hero Name: <input [(ngModel)]="name"> {{ name }}</div><br><br><button type="button" [attr.aria-label]="help">help</button><br><br><div [class.special]="isSpecial">Special</div><br><br><button type="button" [style.color]="isSpecial ? 'red' : 'green'">button</button><a class="to-toc" href="#toc">top</a><!-- property vs. attribute --><hr><h2 id="prop-vs-attrib">Property vs. Attribute (img examples)</h2><!-- examine the following <img> tag in the browser tools --><img src="images/ng-logo.svg" [src]="heroImageUrl" [alt]="hero.name"><br><br><img [src]="iconUrl" alt="icon"/><img [attr.src]="villainImageUrl" alt="villain-image"/><a class="to-toc" href="#toc">top</a><!-- buttons --><hr><h2 id="buttons">Buttons</h2><button>Enabled (but does nothing)</button><button type="button" disabled>Disabled</button><button type="button" disabled=false>Still disabled</button><br><br><button type="button" disabled>disabled by attribute</button><button type="button" [disabled]="isUnchanged">disabled by property binding</button><br><br><button type="button" [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button><a class="to-toc" href="#toc">top</a><!-- property binding --><hr><h2 id="property-binding">Property Binding</h2><img [alt]="hero.name" [src]="heroImageUrl"><button type="button" [disabled]="isUnchanged">Cancel is disabled</button><div [ngClass]="classes">[ngClass] binding to the classes property</div><app-hero-detail [hero]="currentHero"></app-hero-detail><!-- ERROR: HeroDetailComponent.hero expects a Hero object, not the string "currentHero" --><!-- <app-hero-detail hero="currentHero"></app-hero-detail> --><app-hero-detail prefix="You are my" [hero]="currentHero"></app-hero-detail><p> <img src="{{ heroImageUrl }}" alt="interpolated image of {{ currentHero.name }}"> is the <em>interpolated</em> image.</p><p> <img [src]="heroImageUrl" [alt]="'property bounded image of ' + currentHero.name"> is the <em>property bound</em> image.</p><p><span>"{{ title }}" is the <em>interpolated</em> title.</span></p><p>"<span [innerHTML]="title"></span>" is the <em>property bound</em> title.</p><!-- Angular generates warnings for these two lines as it sanitizes them WARNING: sanitizing HTML stripped some content (see https://g.co/ng/security#xss). --><p><span>"{{evilTitle}}" is the <em>interpolated</em> evil title.</span></p><p>"<span [innerHTML]="evilTitle"></span>" is the <em>property bound</em> evil title.</p><a class="to-toc" href="#toc">top</a><!-- attribute binding --><hr><h2 id="attribute-binding">Attribute Binding</h2><!-- create and set a colspan attribute --><table border=1> <!-- expression calculates colspan=2 --> <tr><td [attr.colspan]="1 + 1">One-Two</td></tr> <!-- ERROR: There is no `colspan` property to set! <tr><td colspan="{{1 + 1}}">Three-Four</td></tr> --> <tr><td>Five</td><td>Six</td></tr></table><br><!-- create and set an aria attribute for assistive technology --><button type="button" [attr.aria-label]="actionName">{{actionName}} with Aria</button><br><br><!-- The following effects are not discussed in the chapter --><div> <!-- any use of [attr.disabled] creates the disabled attribute --> <button type="button" [attr.disabled]="isUnchanged">Disabled</button> <button type="button" [attr.disabled]="!isUnchanged">Disabled as well</button> <!-- we'd have to remove it with property binding --> <button type="button" disabled [disabled]="false">Enabled (but inert)</button></div><a class="to-toc" href="#toc">top</a><!-- class binding --><hr><h2 id="class-binding">Class Binding</h2><!-- standard class attribute setting --><div class="bad curly special">Bad curly special</div><!-- reset/override all class names with a binding --><div class="bad curly special" [class]="badCurly">Bad curly</div><!-- toggle the "special" class on/off with a property --><div [class.special]="isSpecial">The class binding is special</div><!-- binding to `class.special` trumps the class attribute --><div class="special" [class.special]="!isSpecial">This one is not so special</div><a class="to-toc" href="#toc">top</a><!--style binding --><hr><h2 id="style-binding">Style Binding</h2><button type="button" [style.color]="isSpecial ? 'red': 'green'">Red</button><button type="button" [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button><button type="button" [style.font-size.em]="isSpecial ? 3 : 1" >Big</button><button type="button" [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button><a class="to-toc" href="#toc">top</a><!-- event binding --><hr><h2 id="event-binding">Event Binding</h2><button type="button" (click)="onSave()">Save</button><div><!-- `myClick` is an event on the custom `ClickDirective` --><div (myClick)="clickMessage=$event" clickable>click with myClick</div>{{clickMessage}}</div><!-- binding to a nested component --><app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-hero-detail><br><app-big-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-big-hero-detail><div class="parent-div" (click)="onClickMe($event)" clickable>Click me <div class="child-div">Click me too!</div></div><!-- Will save only once --><div (click)="onSave()" clickable> <button type="button" (click)="onSave($event)">Save, no propagation</button></div><!-- Will save twice --><div (click)="onSave()" clickable> <button type="button" (click)="onSave()">Save w/ propagation</button></div><a class="to-toc" href="#toc">top</a><hr><h2 id="two-way">Two-way Binding</h2><div id="two-way-1"> <app-sizer [(size)]="fontSizePx"></app-sizer> <div [style.font-size.px]="fontSizePx">Resizable Text</div> <label for="font-size">FontSize (px): <input id="font-size" [(ngModel)]="fontSizePx"></label></div><br><div id="two-way-2"> <h3>De-sugared two-way binding</h3> <app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer></div><a class="to-toc" href="#toc">top</a><!-- Two way data binding unwound; passing the changed display value to the event handler via `$event` --><hr><h2 id="ngModel">NgModel (two-way) Binding</h2><h3>Result: {{ currentHero.name }}</h3><input [value]="currentHero.name" (input)="updateCurrentHeroName($event)">without NgModel<br><input [(ngModel)]="currentHero.name">[(ngModel)]<br><input [ngModel]="currentHero.name" (ngModelChange)="currentHero.name=$event">(ngModelChange)="...name=$event"<br><input [ngModel]="currentHero.name" (ngModelChange)="setUppercaseName($event)">(ngModelChange)="setUppercaseName($event)"<a class="to-toc" href="#toc">top</a><!-- NgClass binding --><hr><h2 id="ngClass">NgClass Binding</h2><p>currentClasses is {{ currentClasses | json }}</p><div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div><!-- not used in chapter --><br><label for="canSave">saveable <input type="checkbox" id="canSave" [(ngModel)]="canSave"></label> |<label for="isUnchanged">modified: <input type="checkbox" id="isUnchanged" [value]="!isUnchanged" (change)="isUnchanged=!isUnchanged"></label> |<label for="isSpecial">special: <input type="checkbox" id="isSpecial" [(ngModel)]="isSpecial"></label><button type="button" (click)="setCurrentClasses()">Refresh currentClasses</button><br><br><div [ngClass]="currentClasses"> This div should be {{ canSave ? "": "not"}} saveable, {{ isUnchanged ? "unchanged" : "modified" }} and, {{ isSpecial ? "": "not"}} special after clicking "Refresh".</div><br><br><div [ngClass]="isSpecial ? 'special' : ''">This div is special</div><div class="bad curly special">Bad curly special</div><div [ngClass]="{'bad':false, 'curly':true, 'special':true}">Curly special</div><a class="to-toc" href="#toc">top</a><!-- NgStyle binding --><hr><h2 id="ngStyle">NgStyle Binding</h2><div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" > This div is x-large or smaller.</div><h3>[ngStyle] binding to currentStyles - CSS property names</h3><p>currentStyles is {{currentStyles | json}}</p><div [ngStyle]="currentStyles"> This div is initially italic, normal weight, and extra large (24px).</div><!-- not used in chapter --><br><label for="canSave">italic: <input id="canSave" type="checkbox" [(ngModel)]="canSave"></label> |<label for="isUnchanged">normal: <input id="isUnchanged" type="checkbox" [(ngModel)]="isUnchanged"></label> |<label for="isSpecial">xlarge: <input id="isSpecial" type="checkbox" [(ngModel)]="isSpecial"></label><button type="button" (click)="setCurrentStyles()">Refresh currentStyles</button><br><br><div [ngStyle]="currentStyles"> This div should be {{ canSave ? "italic": "plain"}}, {{ isUnchanged ? "normal weight" : "bold" }} and, {{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".</div><a class="to-toc" href="#toc">top</a><!-- NgIf binding --><hr><h2 id="ngIf">NgIf Binding</h2>@if (isActive) { <app-hero-detail></app-hero-detail>}@if (currentHero) { <div>Hello, {{ currentHero.name }}</div>}@if (nullHero) { <div>Hello, {{ nullHero.name }}</div>}<!-- NgIf binding with template (no *) --><ng-template [ngIf]="currentHero">Add {{currentHero.name}} with template</ng-template><!-- Does not show because isActive is false! --><div>Hero Detail removed from DOM (via template) because isActive is false</div><ng-template [ngIf]="isActive"> <app-hero-detail></app-hero-detail></ng-template><!-- isSpecial is true --><div [class.hidden]="!isSpecial">Show with class</div><div [class.hidden]="isSpecial">Hide with class</div><!-- HeroDetail is in the DOM but hidden --><app-hero-detail [class.hidden]="isSpecial"></app-hero-detail><div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div><div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div><a class="to-toc" href="#toc">top</a><!-- NgFor binding --><hr><h2 id="ngFor">NgFor Binding</h2><div class="box"> @for (hero of heroes; track hero) { <div>{{ hero.name }}</div> }</div><br><div class="box"> <!-- *ngFor w/ hero-detail Component --> @for (hero of heroes; track hero) { <app-hero-detail [hero]="hero"></app-hero-detail> }</div><a class="to-toc" href="#toc">top</a><h4 id="ngFor-index">*ngFor with index</h4><p>with <em>semi-colon</em> separator</p><div class="box"> @for (hero of heroes; track hero; let i = $index) { <div>{{ i + 1 }} - {{ hero.name }}</div> }</div><p>with <em>comma</em> separator</p><div class="box"> <!-- Ex: "1 - Hercules" --> @for (hero of heroes; track hero; let i = $index) { <div>{{ i + 1 }} - {{ hero.name }}</div> }</div><a class="to-toc" href="#toc">top</a><h4 id="ngFor-trackBy">*ngFor trackBy</h4><button type="button" (click)="resetHeroes()">Reset heroes</button><button type="button" (click)="changeIds()">Change ids</button><button type="button" (click)="clearTrackByCounts()">Clear counts</button><p><em>without</em> trackBy</p><div class="box"> @for (hero of heroes; track hero) { <div #noTrackBy>({{ hero.id }}) {{ hero.name }}</div> } @if (heroesNoTrackByCount) { <div id="noTrackByCnt" > Hero DOM elements change #{{ heroesNoTrackByCount }} without trackBy </div> }</div><p>with trackBy</p><div class="box"> @for (hero of heroes; track trackByHeroes($index, hero)) { <div #withTrackBy>({{ hero.id }}) {{ hero.name }}</div> } @if (heroesWithTrackByCount) { <div id="withTrackByCnt"> Hero DOM elements change #{{heroesWithTrackByCount}} with trackBy </div> }</div><br><br><br><p>with trackBy and <em>semi-colon</em> separator</p><div class="box"> @for (hero of heroes; track trackByHeroes($index, hero)) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with trackBy and <em>comma</em> separator</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with trackBy and <em>space</em> separator</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><p>with <em>generic</em> trackById function</p><div class="box"> @for (hero of heroes; track hero) { <div>({{ hero.id }}) {{ hero.name }}</div> }</div><a class="to-toc" href="#toc">top</a><!-- NgSwitch binding --><hr><h2 id="ngSwitch">NgSwitch Binding</h2><p>Pick your favorite hero</p><div> @for (h of heroes; track h) { <label for="hero-{{ h }}"> <input id="hero-{{ h }}" type="radio" name="heroes" [(ngModel)]="currentHero" [value]="h">{{ h.name }} </label> }</div><div> @switch (currentHero.emotion) { @case ('happy') { <app-happy-hero [hero]="currentHero"></app-happy-hero> } @case ('sad') { <app-sad-hero [hero]="currentHero"></app-sad-hero> } @case ('confused') { <app-confused-hero [hero]="currentHero"></app-confused-hero> } @case ('confused') { <div>Are you as confused as {{ currentHero.name }}?</div> } @default { <app-unknown-hero [hero]="currentHero"></app-unknown-hero> } }</div><a class="to-toc" href="#toc">top</a><!-- template reference variable --><hr><h2 id="ref-vars">Template reference variables</h2><input #phone placeholder="phone number"><!-- lots of other elements --><!-- phone refers to the input element; pass its `value` to an event handler --><button type="button" (click)="callPhone(phone.value)">Call</button><!-- btn refers to the button element; show its disabled state --><button type="button" #btn disabled [innerHTML]="'disabled by attribute: '+btn.disabled"></button><h4>Example Form</h4><app-hero-form [hero]="currentHero"></app-hero-form><a class="to-toc" href="#toc">top</a><!-- inputs and output --><hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2><img alt="icon" [src]="iconUrl"/><button type="button" (click)="onSave()">Save</button><app-hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)"></app-hero-detail><div (myClick)="clickMessage2=$event" clickable>myClick2</div>{{ clickMessage2 }}<a class="to-toc" href="#toc">top</a><!-- Pipes --><hr><h2 id="pipes">Pipes</h2><div>Title through uppercase pipe: {{ title | uppercase }}</div><!-- Pipe chaining: convert title to uppercase, then to lowercase --><div> Title through a pipe chain: {{ title | uppercase | lowercase }}</div><!-- pipe with configuration argument => "February 25, 1970" --><div>Birthdate: {{ currentHero.birthdate | date:'longDate' }}</div><div>{{ currentHero | json }}</div><div>Birthdate: {{ (currentHero.birthdate | date:'longDate') | uppercase }}</div><div> <!-- pipe price to USD and display the $ symbol --> <span>Price: </span>{{ product.price | currency:'USD':'symbol' }}</div><a class="to-toc" href="#toc">top</a><!-- Null values and the safe navigation operator --><hr><h2 id="safe-navigation-operator">Safe navigation operator <em>?.</em></h2><div> The title is {{ title }}</div><div> The current hero's name is {{ currentHero.name }}</div><div> The current hero's name is {{ currentHero.name }}</div><!--The null hero's name is {{ nullHero.name }}See console log: TypeError: Cannot read property 'name' of null in [null]--><!--No hero, div not displayed, no error -->@if (nullHero) { <div>The null hero's name is {{ nullHero.name }}</div>}<div>The null hero's name is {{ nullHero && nullHero.name }}</div><div> <!-- No hero, no problem! --> The null hero's name is {{ nullHero?.name }}</div><a class="to-toc" href="#toc">top</a><!-- non-null assertion operator --><hr><h2 id="non-null-assertion-operator">Non-null assertion operator <em>!.</em></h2><div> <!--No hero, no text --> @if (hero) { <div>The hero's name is {{ hero!.name }}</div> }</div><a class="to-toc" href="#toc">top</a><!-- non-null assertion operator --><hr><h2 id="any-type-cast-function">$any type cast function <em>$any( )</em>.</h2><div> <!-- Accessing an undeclared member --> <div> The hero's marker is {{ $any(hero).marker }} </div></div><div> <!-- Accessing an undeclared member --> <div> Undeclared members is {{ $any(this).member }} </div></div><a class="to-toc" href="#toc">top</a><!-- TODO: discuss this in the Style binding section --><!-- enums in bindings --><hr><h2 id="enums">Enums in binding</h2><p> The name of the Color.Red enum is {{ Color[Color.Red] }}.<br> The current color is {{ Color[color] }} and its number is {{ color }}.<br> <button type="button" [style.color]="Color[color]" (click)="colorToggle()">Enum Toggle</button></p><a class="to-toc" href="#toc">top</a><hr><h2 id="svg-templates">SVG Templates</h2><app-svg></app-svg><a class="to-toc" href="#toc">top</a>
この例では、$event
オブジェクト、hero
、#heroForm
のコンテキストはテンプレートです。
テンプレートコンテキスト名は、コンポーネントコンテキスト名より優先されます。
前述の deleteHero(hero)
では、hero
はコンポーネントの hero
プロパティではなく、テンプレート入力変数です。
ステートメントのベストプラクティス
プラクティス | 詳細 |
---|---|
簡潔さ | メソッド呼び出しや基本的なプロパティ代入を使用して、テンプレートステートメントを最小限に抑えます。 |
コンテキスト内での作業 | テンプレートステートメントのコンテキストは、コンポーネントクラスインスタンスまたはテンプレートです。このため、テンプレートステートメントは window や document などのグローバル名前空間にあるものを参照できません。たとえば、テンプレートステートメントは console.log() や Math.max() を呼び出すことはできません。 |