Angular2文件學習的知識點摘要——模板語法
目錄
從使用模型-檢視-控制器 (MVC) 或模型-檢視-檢視模型 (MVVM) 的經驗中,很多開發人員都熟悉了元件和模板這兩個概念。 在 Angular 中,元件扮演著控制器或檢視模型的角色,模板則扮演檢視的角色。
HTML
- HTML 是 Angular 模板的語言。
- 幾乎所有的 HTML 語法都是有效的模板語法。但值得注意的例外是
插值表示式
插值表示式可以把計算後的字串插入到 HTML 元素標籤內的文字或對標籤的屬性進行賦值。
模板表示式
- 模板表示式產生一個值。 Angular 執行這個表示式,並把它賦值給繫結目標的屬性,這個繫結目標可能是 HTML 元素、元件或指令。
- 當我們寫{{1 + 1}}時,是往插值表示式的括號中放進了一個模板表示式。 在屬性繫結中會再次看到模板表示式,它出現在=右側的引號中,看起來像這樣:[property]=”expression”。
表示式上下文
- 模板表示式不能引用全域性名稱空間中的任何東西。 不能引用window或document。不能呼叫console.log或Math.max。 它們被侷限於只能訪問來自表示式上下文中的成員。
- 典型的表示式上下文就是這個元件例項,它是各種繫結值的來源。
- 通常,元件本身就是表示式的上下文,這種情況下,模板表示式會引用那個元件。
- 表示式的上下文可以包括元件之外的物件。模板引用變數就是備選的上下文物件之一。
表示式指南
- 模板表示式除了目標屬性的值以外,不應該改變應用的任何狀態。
- Angular 執行模板表示式比我們想象的頻繁。表示式應該快速結束。當計算代價較高時,應該考慮快取那些從其它值計算得出的值。
- 模板表示式應該非常簡單。雖然可以寫出相當複雜的模板表示式,但不要那麼去寫。
- 最好使用冪等的表示式,因為它沒有副作用,並且能提升 Angular 變更檢測的效能。
- 在 Angular 的術語中,冪等的表示式應該總是返回完全相同的東西,直到某個依賴值發生改變。
模板語句
- 模板語句用來響應由繫結目標(如 HTML 元素、元件或指令)觸發的事件。
- 模板語句將在事件繫結一節看到,它出現在=號右側的引號中,就像這樣:(event)=”statement”。
- 響應事件是 Angular 中“單向資料流”的另一面。 在一次事件迴圈中,可以隨意改變任何地方的任何東西。
- 模板語句解析器和模板表示式解析器有所不同,特別之處在於它支援基本賦值 (=) 和表示式鏈 (;和,)。
語句上下文
- 和表示式中一樣,語句只能引用語句上下文中 —— 通常是正在繫結事件的那個元件例項。
- 模板語句無法引用全域性名稱空間的任何東西。它們不能引用window或者document, 不能呼叫console.log或者Math.max。
- (click)=”onSave()”中的 onSave 就是資料繫結元件例項中的方法。
- 語句上下文可以包含元件之外的物件。模板引用物件就是備選上下文物件之一。 在事件繫結語句中,經常會看到被保留的
$event
符號,它代表觸發事件的“訊息”或“有效載荷”。
語句指南
和表示式一樣,避免寫複雜的模板語句。 常規是函式呼叫或者屬性賦值。
繫結語法
根據資料流的方向,可以把所有繫結歸為三類。
資料方向 | 語法 | 繫結型別 |
---|---|---|
單向 從資料來源到檢視目標 |
{{expression}} [target] = “expression” bind-target = “expression” |
插值表示式、Property、Attribute、類、樣式 |
單向 從檢視目標到資料來源 |
(target) = “statement” on-target=”statement” |
事件 |
雙向 | [(target)] = “expression” bindon-target = “expression” |
雙向 |
新的思維模型
- 模板繫結是通過 property 和事件來工作的,而不是 attribute。
- 在 Angular 的世界中,attribute 唯一的作用是用來初始化元素和指令的狀態。 當進行資料繫結時,只是在與元素和指令的 property 和事件打交道,而 attribute 就完全靠邊站了。
HTML attribute 與 DOM property 的對比
1. attribute 是由 HTML 定義的。property 是由 DOM (Document Object Model) 定義的。
2. attribute 初始化 DOM property,然後它們的任務就完成了。property 的值可以改變;attribute 的值不能改變。
例如,當瀏覽器渲染<input type="text" value="Bob">
時,它將建立相應 DOM 節點, 其value property 被初始化為 “Bob”。
當用戶在輸入框中輸入 “Sally” 時,DOM 元素的value property 變成了 “Sally”。 但是這個 HTML value attribute 保持不變。如果我們讀取 input 元素的 attribute,就會發現確實沒變: input.getAttribute(‘value’) // 返回 “Bob”。
HTML attribute value指定了初始值;DOM value property 是當前值。
disabled
attribute 是另一個古怪的例子。按鈕的disabled property 是false,因為預設情況下按鈕是可用的。 當我們新增disabled attribute 時,只要它出現了按鈕的disabled property 就初始化為true,於是按鈕就被禁用了。
新增或刪除disabled attribute會禁用或啟用這個按鈕。但 attribute 的值無關緊要,這就是我們為什麼沒法通過 仍被禁用這種寫法來啟用按鈕。
設定按鈕的disabled property(如,通過 Angular 繫結)可以禁用或啟用這個按鈕。 這就是 property 的價值。
3. 就算名字相同,HTML attribute 和 DOM property 也不是同一樣東西。
繫結目標
資料繫結的目標是 DOM 中的某些東西。 這個目標可能是(元素 | 元件 | 指令的)property、(元素 | 元件 | 指令的)事件,或(極少數情況下) attribute 名。
繫結型別 | 目標 | 範例 |
---|---|---|
Property | 元素的 property 元件的 property 指令的 property |
<img [src] = "heroImageUrl"> <hero-detail [hero]="currentHero"></hero-detail> <div [ngClass] = "{selected: isSelected}"></div> |
事件 | 元素的事件 元件的事件 指令的事件 |
<button (click) = "onSave()">Save</button> <hero-detail (deleteRequest)="deleteHero()"></hero-detail> <div (myClick)="clicked=$event">click me</div> |
雙向 | 事件與 property | <input [(ngModel)]="heroName"> |
Attribute | attribute(例外情況) | <button [attr.aria-label]="help">help</button> |
CSS 類 | class property | <div [class.special]="isSpecial">Special</div> |
樣式 | style property | <button [style.color] = "isSpecial ? 'red' : 'green'"> |
屬性 (property) 繫結
- 當要把檢視元素的屬性 (property) 設定為模板表示式時,就要寫模板的屬性 (property) 繫結。
- 最常用的屬性繫結是把元素屬性設定為元件屬性的值。如:
<img [src]="heroImageUrl">
。 - 另一個例子是設定指令的屬性:
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
。 - 還有另一個例子是設定自定義元件的模型屬性(這是父子元件之間通訊的重要途徑):
<hero-detail [hero]="currentHero"></hero-detail>
。
單向輸入
人們經常把屬性繫結描述成單向資料繫結,因為值的流動是單向的,從元件的資料屬性流動到目標元素的屬性。
繫結目標
- 包裹在方括號中的元素屬性名標記著目標屬性。如:
<img [src]="heroImageUrl">
的src
屬性。 - 有些人喜歡用
bind-
字首的可選形式,並稱之為規範形式:<img bind-src="heroImageUrl">
。 - 目標的名字總是 property 的名字。即使它看起來和別的名字一樣。 看到src時,可能會把它當做 attribute。不!它不是!它是 image 元素的 property 名。
- 元素屬性可能是最常見的繫結目標,但 Angular 會先去看這個名字是否是某個已知指令的屬性名。
- 如果名字沒有匹配上已知指令或元素的屬性,Angular 就會報告“未知指令”的錯誤。
消除副作用
- 不能在屬性繫結表示式中對任何東西賦值,也不能使用自增、自減運算子。
- 當然,表示式可能會呼叫具有副作用的屬性或方法。但 Angular 沒法知道這一點,也沒法阻止我們。
- 一般建議是,只繫結資料屬性和那些只返回值而不做其它事情的方法。
注意點
- 模板表示式應該返回目標屬性所需型別的值。 如果目標屬性想要個字串,就返回字串。 如果目標屬性想要個數字,就返回數字。 如果目標屬性想要個物件,就返回物件。
- 方括號告訴 Angular 要計算模板表示式。 如果忘了加方括號,Angular 會把這個表示式當做字串常量看待,並用該字串來初始化目標屬性。 它不會計算這個字串。
- 不管是插值表示式還是屬性繫結,Angular都不會允許帶有 script 標籤的 HTML 洩漏到瀏覽器中。
attribute、class 和 style 繫結
attribute 繫結
- 可以通過attribute 繫結來直接設定 attribute 的值。
- 當元素沒有屬性可綁的時候,就必須使用 attribute 繫結。如: table 中的 colspan/rowspan 等 attribute。 它們是純粹的 attribute,沒有對應的屬性可供繫結。
- 插值表示式和屬性繫結只能設定屬性,不能設定 attribute。
- attribute 繫結的語法與屬性繫結類似。 但方括號中的部分不是元素的屬性名,而是由attr字首,一個點 (.) 和 attribute 的名字組成。 可以通過值為字串的表示式來設定 attribute 的值。
- 這裡把
[attr.colspan]
繫結到一個計算值:
<table border=1>
<!-- expression calculates colspan=2 -->
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
<!-- ERROR: There is no `colspan` property to set!
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
-->
<tr><td>Five</td><td>Six</td></tr>
</table>
CSS類繫結
<!-- reset/override all class names with a binding -->
<div class="bad curly special"
[class]="badCurly">Bad curly</div>
說明:當 badCurly 有值時 class 這個 attribute 設定的內容會被完全覆蓋。
<!-- 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>
說明:可以繫結到特定的類名。 當模板表示式的求值結果是真值時,Angular 會新增這個類,反之則移除它。
注:雖然這是切換單一類名的好辦法,但我們通常更喜歡使用 NgClass指令 來同時管理多個類名。
樣式繫結
<button [style.color] = "isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
根據條件用 “em” 和 “%” 來設定字型大小的單位。
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
注:雖然這是設定單一樣式的好辦法,但我們通常更喜歡使用 NgStyle指令 來同時設定多個內聯樣式。
注意,樣式屬性命名方法可以用中線命名法,像上面的一樣 也可以用駝峰式命名法,如fontSize
。
事件繫結
事件繫結語法由等號左側帶圓括號的目標事件和右側引號中的模板語句組成。
<button (click)="onSave()">Save</button>
目標事件
圓括號中的名稱 —— 比如(click)
—— 標記出目標事件。
<button (click)="onSave()">Save</button>
有些人更喜歡帶on-字首的備選形式,稱之為規範形式:
<button on-click="onSave()">On Save</button>
元素事件可能是更常見的目標,但 Angular 會先看這個名字是否能匹配上已知指令的事件屬性。
<!-- `myClick` is an event on the custom `ClickDirective` -->
<div (myClick)="clickMessage=$event">click with myClick</div>
如果這個名字沒能匹配到元素事件或已知指令的輸出屬性,Angular 就會報“未知指令”錯誤。
$event和事件處理語句
- 繫結會通過名叫
$event
的事件物件傳遞關於此事件的資訊(包括資料值)。 - 事件物件的形態取決於目標事件。如果目標事件是原生 DOM 元素事件, $event就是 DOM事件物件,它有像
target
和target.value
這樣的屬性。
<input [value]="currentHero.firstName"
(input)="currentHero.firstName=$event.target.value" >
使用 EventEmitter 實現自定義事件
- 通常,指令使用 Angular EventEmitter 來觸發自定義事件。
- 指令建立一個EventEmitter例項,並且把它作為屬性暴露出來。 指令呼叫EventEmitter.emit(payload)來觸發事件,可以傳入任何東西作為訊息載荷。 父指令通過繫結到這個屬性來監聽事件,並通過$event物件來訪問載荷。
雙向資料繫結
- Angular提供了一種特殊的雙向資料繫結語法:
[(x)]
。[(x)]
語法結合了屬性繫結的方括號[x]
和事件繫結的圓括號(x)
。 - 雙向繫結語法實際上是屬性繫結和事件繫結的語法糖。
如:自定義元件SizerComponent
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'my-sizer',
template: `
<div>
<button (click)="dec()" title="smaller">-</button>
<button (click)="inc()" title="bigger">+</button>
<label [style.font-size.px]="size">FontSize: {{size}}px</label>
</div>`
})
export class SizerComponent {
@Input() size: number | string;
@Output() sizeChange = new EventEmitter<number>();
dec() { this.resize(-1); }
inc() { this.resize(+1); }
resize(delta: number) {
this.size = Math.min(40, Math.max(8, +this.size + delta));
this.sizeChange.emit(this.size);
}
}
在父元件AppComponent中可以這麼使用:
<my-sizer [(size)]="fontSizePx"></my-sizer>
<div [style.font-size.px]="fontSizePx">Resizable Text</div>
Angular將SizerComponent的繫結分解成這樣:
<my-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></my-sizer>
這很容易理解:通過使用父元件AppComponent中的fontSizePx屬性初始化SizerComponent元件中的size屬性。當SizerComponent元件中的size屬性發生變化時,就呼叫sizeChange事件的emit方法傳遞改變後的值。$event變數就包含了SizerComponent.sizeChange事件的荷載,即被emi方法傳遞過來的值。然後被重新賦值給父元件的fontSizePx屬性。
注:我們希望能在像和這樣的 HTML 元素上使用雙向資料繫結。 可惜,原生 HTML 元素不遵循x值和xChange事件的模式。幸運的是,Angular 以 NgModel 指令為橋樑,允許在表單元素上使用雙向資料繫結。
使用NgModel進行雙向資料繫結
在使用ngModel做雙向資料繫結之前,得先匯入FormsModule。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
[(ngModel)]
內幕
<input [(ngModel)]="currentHero.firstName">
可以通過分別繫結元素的value屬性和`input事件來實現同樣的效果:
<input [value]="currentHero.firstName"
(input)="currentHero.firstName=$event.target.value" >
ngModel指令通過它自己的ngModel輸入屬性和ngModelChange輸出屬性隱藏了這些繁瑣的細節。
<input
[ngModel]="currentHero.firstName"
(ngModelChange)="currentHero.firstName=$event">
注:ngModel
資料屬性設定元素的 value 屬性,ngModelChange
事件屬性監聽元素 value 的變化。
其他應用,如使用ngModel的展開形式,強制讓輸入值變成大寫形式:
<input
[ngModel]="currentHero.firstName"
(ngModelChange)="setUpperCaseFirstName($event)">
內建指令
NgClass
CSS 類繫結(上述有提到過) 是新增或刪除單個類的最佳途徑。
<!-- toggle the "special" class on/off with a property -->
<div [class.special]="isSpecial">The class binding is special</div>
當想要同時新增或移除多個 CSS 類時,NgClass指令可能是更好的選擇。
繫結到NgClass的值時一個 key:value形式的控制物件,這個物件中的每個key都是一個CSS類名,如果它的value為true,這個類就會被加上,否則就會被移除。如:
setClasses() {
let classes = {
saveable: this.canSave, // true
modified: !this.isUnchanged, // false
special: this.isSpecial, // true
};
return classes;
}
使用NgClass屬性繫結:
<div [ngClass]="setClasses()">This div is saveable and special</div>
NgStyle
樣式繫結(上述有提到過)是設定單一樣式值的簡單方式。
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
This div is x-large.
</div>
如果要同時設定多個內聯樣式,NgStyle指令可能是更好的選擇。
NgStyle需要繫結到一個 key:value 控制物件。 物件的每個 key 是樣式名,它的 value 是能用於這個樣式的任何值。如:
setStyles() {
let styles = {
// CSS property names
'font-style': this.canSave ? 'italic' : 'normal', // italic
'font-weight': !this.isUnchanged ? 'bold' : 'normal', // normal
'font-size': this.isSpecial ? '24px' : '8px', // 24px
};
return styles;
}
<div [ngStyle]="setStyles()">
This div is italic, normal weight, and extra large (24px).
</div>
NgIf
通過繫結NgIf
指令到真值表達式,可以把元素子樹(元素及其子元素)新增到 DOM 上。繫結到假值表示式將從 DOM 中移除元素子樹。
<div *ngIf="currentHero">Hello, {{currentHero.firstName}}</div>
NgSwitch
當需要從一組可能的元素樹中根據條件顯示一個時,我們就把它繫結到NgSwitch。 Angular 將只把選中的元素樹放進 DOM 中。
<span [ngSwitch]="toeChoice">
<span *ngSwitchCase="'Eenie'">Eenie</span>
<span *ngSwitchCase="'Meanie'">Meanie</span>
<span *ngSwitchCase="'Miney'">Miney</span>
<span *ngSwitchCase="'Moe'">Moe</span>
<span *ngSwitchDefault>other</span>
</span>
注:不要在ngSwitch的前面加星號 (),而應該用屬性繫結。要把星號 () 放在ngSwitchCase和ngSwitchDefault的前面。
NgFor
使用同一模板渲染多個條目:
<div *ngFor="let hero of heroes">{{hero.fullName}}</div>
<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>
NgFor 微語法
賦值給*ngFor的字串不是模板表示式。 它是一個微語法 —— 由 Angular 自己解釋的小型語言。在這個例子中,字串”let hero of heroes”的含義是:取出heroes陣列中的每個英雄,把它存入區域性變數hero中,並在每次迭代時對模板 HTML 可用。
hero前面的let關鍵字建立了名叫hero的模板輸入變數。
注:模板輸入變數和模板引用變數不是一回事!
帶索引的 NgFor
ngFor指令支援可選的index,它在迭代過程中會從 0 增長到“陣列的長度”。 可以通過模板輸入變數來捕獲這個 index,並在模板中使用。
<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.fullName}}</div>
NgForTrackBy
trackByHeroes(index: number, hero: Hero) { return hero.id; }
現在,把NgForTrackBy指令設定為那個追蹤函式。
<div *ngFor="let hero of heroes; trackBy:trackByHeroes">({{hero.id}}) {{hero.fullName}}</div>
追蹤函式不會阻止所有 DOM 更改。 如果同一個英雄的屬性變化了,Angular 就可能不得不更新DOM元素。 但是如果這個屬性沒有變化 —— 而且大多數時候它們不會變化 —— Angular 就能留下這些 DOM 元素。列表介面就會更加平滑,提供更好的響應。
* 與 <template>
- *是一種語法糖,它讓那些需要藉助模板來修改 HTML 佈局的指令更易於讀寫。 NgFor、NgIf和NgSwitch都會新增或移除元素子樹,這些元素子樹被包裹在標籤中。
- 我們沒有看到
<template>
標籤,那是因為這種*字首語法讓我們忽略了這個標籤, 而把注意力直接聚焦在所要包含、排除或重複的那些 HTML 元素上。
展開*ngIf
把*
字首語法展開成 template 語法:
<hero-detail *ngIf="currentHero" [hero]="currentHero"></hero-detail>
展開的第一步是把ngIf(沒有*字首)和它的內容傳給表示式,再賦值給template指令。
<hero-detail template="ngIf:currentHero" [hero]="currentHero"></hero-detail>
下一步,也是最後一步,是把 HTML 包裹進標籤和[ngIf]屬性繫結中:
<template [ngIf]="currentHero">
<hero-detail [hero]="currentHero"></hero-detail>
</template>
展開*ngSwitch
<span [ngSwitch]="toeChoice">
<!-- with *NgSwitch -->
<span *ngSwitchCase="'Eenie'">Eenie</span>
<span *ngSwitchCase="'Meanie'">Meanie</span>
<span *ngSwitchCase="'Miney'">Miney</span>
<span *ngSwitchCase="'Moe'">Moe</span>
<span *ngSwitchDefault>other</span>
<!-- with <template> -->
<template [ngSwitchCase]="'Eenie'"><span>Eenie</span></template>
<template [ngSwitchCase]="'Meanie'"><span>Meanie</span></template>
<template [ngSwitchCase]="'Miney'"><span>Miney</span></template>
<template [ngSwitchCase]="'Moe'"><span>Moe</span></template>
<template ngSwitchDefault><span>other</span></template>
</span>
展開*ngFor
<hero-detail *ngFor="let hero of heroes; trackBy:trackByHeroes" [hero]="hero"></hero-detail>
<hero-detail template="ngFor let hero of heroes; trackBy:trackByHeroes" [hero]="hero"></hero-detai
<template ngFor let-hero [ngForOf]="heroes" [ngForTrackBy]="trackByHeroes">
<hero-detail [hero]="hero"></hero-detail>
</template>
模板引用變數
- 模板引用變數是模板中對 DOM 元素或指令的引用。
- 它能在原生 DOM 元素中使用,也能用於 Angular 元件 —— 實際上,它可以和任何自定義 Web 元件協同工作。
- 可以在同一元素、兄弟元素或任何子元素中引用模板引用變數。
- 不要在同一個模版中多次定義相同變數名,否則執行時的值將會不可預測。
- 關於建立和使用模板引用變數的另外兩個例子:
<!-- phone refers to the input element; pass its `value` to an event handler -->
<input #phone placeholder="phone number">
<button (click)="callPhone(phone.value)">Call</button>
<!-- fax refers to the input element; pass its `value` to an event handler -->
<input ref-fax placeholder="fax number">
<button (click)="callFax(fax.value)">Fax</button>
“phone” 的井號 (#) 字首表示定義了一個phone變數。
注:有些人不喜歡使用#字元,而是使用它的規範形式:ref-字首。 例如,既能用#phone,也能用ref-phone來定義phone變數。
6. Angular把模板引用變數的值設定為它所在那個元素。
NgForm和模板引用變數
表單,使用模板引用變數的典範——簡化過的範例
<form (ngSubmit)="onSubmit(theForm)" #theForm="ngForm">
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" name="name" required [(ngModel)]="currentHero.firstName">
</div>
<button type="submit" [disabled]="!theForm.form.valid">Submit</button>
</form>
theForm
變數的值是什麼?
實際上它是個ngForm,對 Angular 內建指令NgForm的引用。 它包裝了原生的HTMLFormElement並賦予它更多超能力,比如跟蹤使用者輸入的有效性。
這解釋了該如何通過檢查theForm.form.valid來禁用提交按鈕,以及如何把一個資訊量略大的物件傳給父元件的onSubmit方法。
輸入輸出屬性
記住:所有元件皆為指令。(此前,提到過:其實元件也是指令,由於元件的特殊,所以被專門提出來。記住這句話將有利於對後文的理解。)
1. 繫結的目標是在=
左側的部分,源則是在=
右側的部分。
2. 繫結的目標是繫結符:[]
、()
或[()]
中的屬性或事件名, 源則是引號 (" "
) 中的部分或插值符號 ({{}}
) 中的部分。
3. 源指令中的每個成員都會自動在繫結中可用。 不需要特別做什麼,就能在模板表示式或語句中訪問指令的成員。(意思就是如:[value]=”hero”,像這樣的繫結,對於=
右側的""
中源,可以使用到指令中的每個成員。(記住:元件也是指令。這裡你可以進行類比。))
4. 訪問目標指令中的成員則受到限制。 只能繫結到那些顯式標記為輸入或輸出的屬性。(那什麼是輸入或輸出屬性呢?如何進行顯式標記呢?)
宣告輸入和輸出屬性
目標屬性必須被顯式的標記為輸入或輸出。
如自定義的hero-detail元件
<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)">
</hero-detail>
這裡的目標屬性有hero和deleteRequest。那麼既然作為目標屬性,那麼就要被顯式地標記為輸入或輸出屬性。
當我們深入HeroDetailComponent內部時,就會看到這些屬性被裝飾器標記成了輸入和輸出屬性。
@Input() hero: Hero;
@Output() deleteRequest = new EventEmitter<Hero>();
另外,還可以在指令元資料的inputs
或outputs
陣列中標記出這些成員。比如這個例子:
@Component({
inputs: ['hero'],
outputs: ['deleteRequest'],
})
注:既可以通過裝飾器,也可以通過元資料陣列來指定輸入/輸出屬性。但別同時用!
輸入還是輸出?
- 輸入屬性通常接收資料值。輸出屬性暴露事件生產者,如
EventEmitter
物件。 - 輸入和輸出這兩個詞是從目標指令的角度來說的。即輸入:向目標指令輸入資料;輸出:從目標指令流出。
- 從HeroDetailComponent角度來看(
<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)"></hero-detail>
),HeroDetailComponent.hero
是個輸入屬性,因為資料流從模板繫結表示式流入那個屬性。HeroDetailComponent.deleteRequest
是個輸出屬性,因為事件從那個屬性流出,流向模板繫結語句中的處理器。
給輸入/輸出屬性起別名
如在使用時輸出屬性為myclick:
<div (myClick)="clickMessage=$event">click with myClick</div>
而實際在指令類中為clicks屬性,只是指定了外部對應的該屬性的別名:
@Output('myClick') clicks = new EventEmitter<string>(); // @Output(alias) propertyName = ...
也可在inputs和outputs陣列中為屬性指定別名。 可以寫一個冒號 (:) 分隔的字串,左側是指令中的屬性名,右側則是公開的別名。
@Directive({
outputs: ['clicks:myClick'] // propertyName:alias
})
模板表示式操作符
模板表示式語言使用了 JavaScript 語法的子集,並補充了幾個用於特定場景的特殊操作符。
管道操作符 ( | )
在繫結之前,表示式的結果可能需要一些轉換。例如,可能希望把數字顯示成金額、強制文字變成大寫,或者過濾列表以及進行排序。
Angular 管道對像這樣的小型轉換來說是個明智的選擇。 管道是一個簡單的函式,它接受一個輸入值,並返回轉換結果。 它們很容易用於模板表示式中,只要使用管道操作符 (|) 就行了。
<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>
json管道對除錯繫結特別有用:
<div>{{currentHero | json}}</div>
安全導航操作符 ( ?. ) 和空屬性路徑
Angular 的安全導航操作符 (?.) 是一種流暢而便利的方式,用來保護出現在屬性路徑中 null 和 undefined 值。 下例中,當currentHero為空時,保護檢視渲染器,讓它免於失敗。
The current hero's name is {{currentHero?.firstName}}
以下類似的方法也可以實現同樣的功能:
通過寫NgIf程式碼來解決這個問題:
<!--No hero, div not displayed, no error -->
<div *ngIf="nullHero">The null hero's name is {{nullHero.firstName}}</div>
或者可以嘗試通過&&來把屬性路徑的各部分串起來,讓它在遇到第一個空值的時候,就返回空。
The null hero's name is {{nullHero && nullHero.firstName}}
這些方法都有價值,但是會顯得笨重,特別是當這個屬性路徑非常長的時候。 想象一下在一個很長的屬性路徑(如a.b.c.d)中對空值提供保護。
Angular 安全導航操作符 (?.) 是在屬性路徑中保護空值的更加流暢、便利的方式。 表示式會在它遇到第一個空值的時候跳出。 顯示是空的,但應用正常工作,而沒有發生錯誤。
<!-- No hero, no problem! -->
The null hero's name is {{nullHero?.firstName}}
在像a?.b?.c?.d
這樣的長屬性路徑中,它工作得很完美。