學習資料來自 Angular.cn 與 Angular.io定页。
模板語法
在 Angular 中迅耘,組件扮演著控制器或視圖模型的角色奸晴,模板則扮演視圖的角色冤馏。
模板中的 HTML
HTML 是 Angular 模板的語言。
<h1>Hello Angular</h1>
為防范腳本注入攻擊的風險寄啼,<script>
元素被禁用了(忽略了)逮光。更多內(nèi)容參見安全代箭。
<html>
、<body>
和 <base>
元素在此沒有使用的意義涕刚。
插值表達式 (Interpolation) {{...}}
插值表達式可以把計算后的字符串插入到 HTML 元素標簽內(nèi)的文本或?qū)撕灥膶傩赃M行賦值嗡综。
一般來說,括號間的素材是一個模板表達式杜漠,Angular 先對它求值极景,再把它轉(zhuǎn)換成字符串。
<!-- "The sum of 1 + 1 is 2" -->
<p>The sum of 1 + 1 is {{1 + 1}}</p>
模板表達式 (Template expressions)
模板表達式產(chǎn)生一個值驾茴。Angular 執(zhí)行這個表達式盼樟,并把它賦值給綁定目標的屬性,這個綁定目標可能是 HTML 元素沟涨、組件或指令恤批。
模板表達式不支持的內(nèi)容包括:
- 賦值 (
=
,+=
,-=
, ...) -
new
運算符 - 使用
;
或,
的鏈式表達式 - 自增或自減操作符 (
++
和--
) - 不支持位運算
|
和&
- 具有新的模板表達式運算符,比如
|
和?.
- 不能引用全局命名空間中的任何東西裹赴。 不能引用
window
或document
喜庞。不能調(diào)用console.log
或Math.max
。
表達式上下文 (Expression context)
典型的表達式上下文是組件實例棋返。
The expression context is typically the component instance.
下例中 title
和 isUnchanged
均為 AppComponent 的屬性延都。
{{title}}
<span [hidden]="isUnchanged">changed</span>
表達式的上下文可以包括組件之外的對象。 比如模板輸入變量 (let hero)和模板引用變量(#heroInput)就是備選的上下文對象之一睛竣。
<div *ngFor="let hero of heroes">{{hero.name}}</div>
<input #heroInput> {{heroInput.value}}
用術(shù)語來說晰房,表達式上下文由模版變量,指令上下文對象(如果存在)和組件成員混合而成射沟。如果所引用的名稱在上述命名空間中有沖突殊者,那么將會按照模板變量,指令上下文和組件成員的順序優(yōu)先選取验夯。
上例展示了一個命名沖突猖吴。組件有一個 hero
屬性,*ngFor
定義了一個 hero
模版變量挥转。{{hero.name}}
中的 hero
指的是模板變量海蔽,而非組件屬性。
模板表達式被局限于只能訪問來自表達式上下文中的成員绑谣。
表達式指南 (Expression guidelines)
模板表達式能成就或毀掉一個應用党窜。應遵循原則:
1. 沒有可見的副作用
模板表達式除了目標屬性的值以外,不應該改變應用的任何狀態(tài)借宵。這條規(guī)則是 Angular “單向數(shù)據(jù)流”策略的基礎(chǔ)幌衣。在一次單獨的渲染過程中,視圖應該總是穩(wěn)定的暇务。
2. 執(zhí)行迅速
Angular 執(zhí)行模板表達式比我們想象的頻繁泼掠。當計算代價較高時怔软,應該考慮緩存那些從其它值計算得出的值。
TODO: 如何緩存择镇?
3. 非常簡單
模板表達式應保持簡單挡逼,不要過于復雜。應在組件中實現(xiàn)應用和業(yè)務邏輯腻豌,使開發(fā)和測試變得更容易家坎。
4. 冪等性
使用冪等的表達式?jīng)]有副作用,并且能提升 Angular 變更檢測的性能吝梅。
在 Angular 的術(shù)語中虱疏,冪等的表達式應該總是返回完全相同的東西,直到某個依賴值發(fā)生改變苏携。
模板語句 (Template statements)
模板語句用來響應由綁定目標(如 HTML 元素做瞪、組件或指令)觸發(fā)的事件。
A template statement responds to an event raised by a binding target such as an element, component, or directive.
語法:(event)="statement"
<button (click)="deleteHero()">Delete hero</button>
模板語句有副作用右冻。這是事件的關(guān)鍵所在装蓬,可以根據(jù)用戶的活動更新應用狀態(tài)。
A template statement has a side effect. That's the whole point of an event. It's how you update application state from user action.
模板語句解析器 (template statement parser) 和模板表達式解析器 (template expression parser) 有所不同纱扭,特別之處在于它支持基本賦值 (=
) 和表達式鏈 ( ;
和 ,
)牍帚。
某些 JavaScript 語法仍然是不允許的:
-
new
運算符 - 自增和自減運算符:
++
和--
- 操作并賦值,例如
+=
和-=
- 位操作符
|
和&
- 模板表達式運算符
語句上下文 (Statement context)
和表達式中一樣乳蛾,語句只能引用語句上下文中——通常是正在綁定事件的那個組件實例暗赶。
語句上下文可以引用模板自身上下文中的屬性。下例把模板的 $event 對象肃叶、模板輸入變量(let hero)和模板引用變量(#heroForm)傳給了組件中的一個事件處理器方法蹂随。
<button (click)="onSave($event)">Save</button>
<button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button>
<form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>
模板語句不能引用全局命名空間的任何東西。比如不能引用 window
或 document
因惭,也不能調(diào)用 console.log
或 Math.max
糙及。
語句指南 (Expression guidelines)
和表達式一樣,避免寫復雜的模板語句筛欢。常規(guī)是函數(shù)調(diào)用或者屬性賦值。
綁定語法:概覽
綁定的類型可以根據(jù)數(shù)據(jù)流的方向分成三類:
- 從數(shù)據(jù)源到視圖(source-to-view)
- 從視圖到數(shù)據(jù)源(view-to-source)
- 雙向的從視圖到數(shù)據(jù)源再到視圖(view-to-source-to-view)唇聘。
單向 source-to-view
語法 | 綁定類型 |
---|---|
{{expression}} |
插值表達式 |
[target]="expression" / bind-target="expression"
|
Property, Attribute 類樣式 |
單向 view-to-source
語法 | 綁定類型 |
---|---|
(target)="statement" / on-target="statement"
|
事件 |
雙向
語法 | 綁定類型 |
---|---|
[(target)]="expression" / bindon-target="expression"
|
雙向 |
綁定類型(插值表達式除外)有一個目標名版姑,它位于等號左邊,它是 Property 的名字(注意它不是 Attribute 的名字)迟郎。
新的思維模型
HTML attribute 與 DOM property 的對比
attribute 是由 HTML 定義的剥险。property 是由 DOM (Document Object Model) 定義的。
- 少量 HTML attribute 和 property 之間有著 1:1 的映射宪肖,如 id表制。
- 有些 HTML attribute 沒有對應的 property健爬,如 colspan。
- 有些 DOM property 沒有對應的 attribute么介,如 textContent娜遵。
<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Save</button>
一旦開始數(shù)據(jù)綁定,就不再跟 HTML attribute 打交道了壤短。這里不是設(shè)置 attribute设拟,而是設(shè)置 DOM 元素、組件和指令的 property久脯。
attribute 初始化 DOM property纳胧,然后它們的任務就完成了。property 的值可以改變帘撰,是當前值跑慕;attribute 的值不能改變,是初始值摧找。
即使名字相同核行,HTML attribute 和 DOM property 也不是同一樣東西。
模板綁定是通過 property 和事件來工作的慰于,而不是 attribute钮科。在 Angular 的世界中,attribute 唯一的作用是用來初始化元素和指令的狀態(tài)婆赠。 當進行數(shù)據(jù)綁定時绵脯,只是在與元素和指令的 property 和事件打交道。
綁定目標 (Binding targets)
數(shù)據(jù)綁定的目標是 DOM 中的某些東西休里。
Property 綁定類型
目標 | 范例 |
---|---|
Element property | <img [src]="heroImageUrl"> |
Component property | <hero-detail [hero]="currentHero"></hero-detail> |
Directive property | <div [ngClass]="{special: isSpecial}"></div> |
事件綁定類型
目標 | 范例 |
---|---|
Element event | <button (click)="onSave()">Save</button> |
Component event | <hero-detail (deleteRequest)="deleteHero()"></hero-detail> |
Directive event | <div (myClick)="clicked=$event" clickable>click me</div> |
雙向綁定類型
目標 | 范例 |
---|---|
Event and property | <input [(ngModel)]="name"> |
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 binding) [property]
當要把視圖元素的屬性 (property) 設(shè)置為模板表達式時蛆挫,就要寫模板的屬性 (property) 綁定。
最常用的屬性綁定是把元素屬性設(shè)置為組件屬性的值妙黍。
<img [src]="heroImageUrl">
上例說明:
image
元素的src
屬性會被綁定到組件的heroImageUrl
屬性上悴侵。
其他示例:
<button [disabled]="isUnchanged">Cancel is disabled</button>
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
<hero-detail [hero]="currentHero"></hero-detail>
單向輸入 (One-way in)
人們經(jīng)常把屬性綁定描述成單向數(shù)據(jù)綁定,因為值的流動是單向的拭嫁,從組件的數(shù)據(jù)屬性流動到目標元素的屬性可免。
注意:
- 不能使用屬性綁定來從目標元素拉取值
- 不能綁定到目標元素的屬性來讀取它。只能設(shè)置它做粤。
- 也不能使用屬性 綁定 來調(diào)用目標元素上的方法浇借。
如果必須讀取目標元素上的屬性或調(diào)用它的某個方法, 參見 API 參考手冊中的 ViewChild 和 ContentChild怕品。
綁定目標
兩種寫法:
<img [src]="heroImageUrl">

bind-
前綴的寫法被稱為規(guī)范形式 (canonical form)妇垢。
元素屬性可能是最常見的綁定目標,但 Angular 會先去看這個名字是否是某個已知指令的屬性名。
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
嚴格來說闯估,Angular 正在匹配指令的輸入屬性的名字灼舍。 這個名字是指令的 inputs
數(shù)組中所列的名字,或者是帶有 @Input()
裝飾器的屬性涨薪。 這些輸入屬性被映射為指令自己的屬性骑素。
Technically, Angular is matching the name to a directive input
, one of the property names listed in the directive's inputs
array or a property decorated with @Input()
. Such inputs map to the directive's own properties.
如果名字沒有匹配上已知指令或元素的屬性,Angular 就會報告“未知指令”的錯誤尤辱。
消除副作用
一般建議是砂豌,只綁定數(shù)據(jù)屬性和那些只返回值而不做其它事情的方法。
返回恰當?shù)念愋?/h3>
模板表達式應該返回目標屬性所需類型的值光督。
別忘了方括號
一次性字符串初始化
當滿足下列條件時阳距,應該省略括號:
- 目標屬性接受字符串值。
- 字符串是個固定值结借,可以直接合并到模塊中筐摘。
- 這個初始值永不改變。
<hero-detail prefix="You are my" [hero]="currentHero"></hero-detail>
屬性綁定還是插值表達式船老?
在多數(shù)情況下咖熟,插值表達式是更方便的備選項畏梆。實際上除破,在渲染視圖之前,Angular 把這些插值表達式翻譯成相應的屬性綁定茁影。
<p> is the <i>interpolated</i> image.</p>
會被自動翻譯成
<p><img [src]="heroImageUrl"> is the <i>property bound</i> image.</p>
<p><span>"{{title}}" is the <i>interpolated</i> title.</span></p>
會被自動翻譯成
<p>"<span [innerHTML]="title"></span>" is the <i>property bound</i> title.</p>
當要渲染的數(shù)據(jù)類型是字符串時薪韩,基于可讀性考慮确沸,建議使用插值表達式。其他情況則必須使用屬性綁定俘陷。
內(nèi)容安全
不管是插值表達式還是屬性綁定罗捎,都不會允許帶有 script
標簽的 HTML 泄漏到瀏覽器中。
插值表達式處理 script
標簽與屬性綁定有所不同拉盾,但是二者都只渲染沒有危害的內(nèi)容桨菜。
evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
<!--Angular generates warnings for these two lines as it sanitizes them
WARNING: sanitizing HTML stripped some content (see http://g.co/ng/security#xss).-->
<p><span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span></p>
<p>"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil title.</p>
attribute、class 和 style 綁定
attribute 綁定
語法:[attr.attribute-name]
可以通過 attribute 綁定來直接設(shè)置 attribute 的值捉偏,因為當元素沒有屬性可綁的時候倒得,就必須使用 attribute 綁定。
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
該語句會報錯如下:
Template parse errors:
Can't bind to 'colspan' since it isn't a known native property
(模板解析錯誤:不能綁定到 '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>
attribute 綁定的主要用例之一是設(shè)置 ARIA attribute
ARIA 指可訪問性屎暇,用于給殘障人士訪問互聯(lián)網(wǎng)提供便利。
<!-- create and set an aria attribute for assistive technology -->
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
CSS 類綁定
語法:[class.class-name]
<!-- 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>
通常更喜歡使用 ngClass
指令來同時管理多個類名驻粟。
樣式綁定
語法:[style.style-property]
<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
帶有單位的綁定:
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
通常更喜歡使用 ngStyle
指令來同時設(shè)置多個內(nèi)聯(lián)樣式。
事件綁定 (Event binding) (event)
語法:(目標事件)="模版語句"
(target event)="template statement"
<button (click)="onSave()">Save</button>
目標事件
元素事件可能是更常見的目標,但 Angular 會先看這個名字是否能匹配上已知指令的事件屬性蜀撑,如:
<!-- `myClick` is an event on the custom `ClickDirective` -->
<div (myClick)="clickMessage=$event" clickable>click with myClick</div>
如果這個名字沒能匹配到元素事件或已知指令的輸出屬性挤巡,Angular 就會報“未知指令”錯誤。
$event 和事件處理語句(event handling statements)
在事件綁定中酷麦,Angular 會為目標事件設(shè)置事件處理器矿卑。當事件發(fā)生時,這個處理器會執(zhí)行模板語句沃饶。典型的模板語句通常涉及到響應事件執(zhí)行動作的接收器母廷,例如從 HTML 控件中取得值,并存入模型糊肤。
In an event binding, Angular sets up an event handler for the target event. When the event is raised, the handler executes the template statement. The template statement typically involves a receiver, which performs an action in response to the event, such as storing a value from the HTML control into a model.
綁定會通過名叫 $event
的事件對象傳遞關(guān)于此事件的信息(包括數(shù)據(jù)值)琴昆。
事件對象的形態(tài)取決于目標事件。
The shape of the event object is determined by the target event.
如果目標事件是原生 DOM 元素事件馆揉, $event
就是 DOM事件對象业舍,它有像 target
和 target.value
這樣的屬性。
<input [value]="currentHero.name"
(input)="currentHero.name=$event.target.value" >
如果事件屬于指令升酣,那么 $event
具體是什么由指令決定舷暮。
使用 EventEmitter 實現(xiàn)自定義事件
通常,指令使用 EventEmitter 來觸發(fā)自定義事件噩茄。指令創(chuàng)建一個 EventEmitter
實例下面,并且把它作為屬性暴露出來。指令調(diào)用 EventEmitter.emit(payload)
來觸發(fā)事件绩聘,可以傳入任何東西作為消息載荷沥割。 父指令通過綁定到這個屬性來監(jiān)聽事件,并通過 $event
對象來訪問載荷君纫。
示例
假設(shè) HeroDetailComponent
用于顯示英雄的信息驯遇,并響應用戶的動作。 雖然 HeroDetailComponent
包含刪除按鈕蓄髓,但它自己并不知道該如何刪除這個英雄叉庐。 最好的做法是觸發(fā)事件來報告“刪除用戶”的請求。
src/app/hero-detail.component.ts (template)
template: `
<div>

<span [style.text-decoration]="lineThrough">
{{prefix}} {{hero?.name}}
</span>
<button (click)="delete()">Delete</button>
</div>`
src/app/hero-detail.component.ts (deleteRequest)
// This component make a request but it can't actually delete a hero.
deleteRequest = new EventEmitter<Hero>();
delete() {
this.deleteRequest.emit(this.hero);
}
說明:組件定義了
deleteRequest
屬性会喝,它是EventEmitter
實例陡叠。 當用戶點擊刪除時,組件會調(diào)用delete()
方法肢执,讓EventEmitter
發(fā)出一個Hero
對象枉阵。
現(xiàn)在,假設(shè)有個宿主的父組件预茄,它綁定了 HeroDetailComponent
的 deleteRequest
事件兴溜。
<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>
當 deleteRequest
事件觸發(fā)時侦厚,Angular 調(diào)用父組件的 deleteHero
方法, 在 $event
變量中傳入要刪除的英雄(來自 HeroDetail
)拙徽。
模板語句有副作用
模板語句的副作用不僅沒問題刨沦,反而正是所期望的。
雙向數(shù)據(jù)綁定 (Two-way binding) [(...)]
語法:[(x)]
[(x)]
語法結(jié)合了屬性綁定的方括號 [x]
和事件綁定的圓括號 (x)
膘怕。當一個元素擁有可以設(shè)置的屬性 x
和對應的事件 xChange
時想诅,就可以使用 [(x)]
語法 。
示例 src/app/sizer.component.ts
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.fontSize
被雙向綁定到 SizerComponent
:
<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>
說明:
$event
變量包含了SizerComponent.sizeChange
事件的荷載岛心。 當用戶點擊按鈕時来破,Angular 將$event
賦值給AppComponent.fontSizePx
。
內(nèi)置指令
內(nèi)置屬性型指令(Built-in attribute directives)
屬性型指令會監(jiān)聽和修改其它 HTML 元素或組件的行為忘古、元素 Attribute徘禁、DOM Property。 它們通常會作為 HTML Attribute 的名稱而應用在元素上存皂。
常見的內(nèi)置屬性型指令:
-
NgClass
添加或移除一組CSS類 -
NgStyle
添加或移除一組CSS樣式 -
NgModel
雙向綁定到 HTML 表單元素
NgClass
示例:組件方法 setCurrentClasses
可以把組件的屬性 currentClasses
設(shè)置為一個對象晌坤,它將會根據(jù)三個其它組件的狀態(tài)為 true
或 false
而添加或移除三個類。
currentClasses: {};
setCurrentClasses() {
// CSS classes: added/removed per current state of component properties
this.currentClasses = {
saveable: this.canSave,
modified: !this.isUnchanged,
special: this.isSpecial
};
}
把 ngClass
屬性綁定到 currentClasses
旦袋,根據(jù)它來設(shè)置此元素的 CSS 類:
<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div>
你既可以在初始化時調(diào)用
setCurrentClassess()
骤菠,也可以在所依賴的屬性變化時調(diào)用。
NgStyle
ngStyle
需要綁定到一個 key:value
控制對象疤孕。 對象的每個 key
是樣式名商乎,它的 value
是能用于這個樣式的任何值。
currentStyles: {};
setCurrentStyles() {
// CSS styles: set per current state of component properties
this.currentStyles = {
'font-style': this.canSave ? 'italic' : 'normal',
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
'font-size': this.isSpecial ? '24px' : '12px'
};
}
<div [ngStyle]="currentStyles">
This div is initially italic, normal weight, and extra large (24px).
</div>
NgModel
要使用 ngModel
需要導入 FormsModule
模塊祭阀。
示例:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular
/* Other imports */
@NgModule({
imports: [
BrowserModule,
FormsModule // <--- import into the NgModule
],
/* Other module metadata */
})
export class AppModule { }
更多關(guān)于
FormsModule
和ngModel
的內(nèi)容參見表單鹉戚。
使用 ngModel
實現(xiàn)雙向數(shù)據(jù)綁定。
<input [(ngModel)]="currentHero.name">
該語句實際上隱藏了其實現(xiàn)細節(jié):
<input [ngModel]="currentHero.name"
(ngModelChange)="currentHero.name=$event">
如果需要做一些不同的處理专控,就不能使用 [(ngModel)]
語法抹凳,而應寫成擴展的方式:
<input [ngModel]="currentHero.name"
(ngModelChange)="setUppercaseName($event)">
ngModel
指令只能用在支持 ControlValueAccessor 的元素上。
內(nèi)置結(jié)構(gòu)型指令(Built-in structural directives)
結(jié)構(gòu)型指令負責 HTML 布局伦腐。
常見的內(nèi)置結(jié)構(gòu)型指令:
-
NgIf
conditionally add or remove an element from the DOM -
NgFor
repeat a template for each item in a list -
NgSwitch
a set of directives that switch among alternative views
NgIf
You can add or remove an element from the DOM by applying an NgIf
directive to that element (called the host elment).
示例:
<hero-detail *ngIf="isActive"></hero-detail>
When the isActive
expression returns a truthy value, NgIf
adds the HeroDetailComponent
to the DOM. When the expression is falsy, NgIf
removes the HeroDetailComponent
from the DOM, destroying that component and all of its sub-components.
別忘了
ngIf
前面的星號(*
)赢底。
這和顯示/隱藏不是一回事。
我們也可以通過類綁定或樣式綁定來顯示或隱藏一個元素柏蘑。但隱藏子樹和用 NgIf
排除子樹是截然不同的幸冻。
當隱藏子樹時,它仍然留在 DOM 中咳焚。當 NgIf
為 false
時 Angular 從 DOM 中物理地移除了子樹洽损,它銷毀了子樹中的組件及其狀態(tài),也潛在釋放了可觀的資源革半。
防范空指針錯誤
NgIf
指令通常會用來防范空指針錯誤碑定。 而顯示/隱藏的方式是無法防范的流码。
<div *ngIf="currentHero">Hello, {{currentHero.name}}</div>
<div *ngIf="nullHero">Hello, {{nullHero.name}}</div>
currentHero
的名字只有當存在 currentHero
時才會顯示出來。 而 nullHero
永遠不會顯示延刘。
NgFor
NgFor
是一個重復器指令——顯示列表項的一種方式旅掂。你先定義了一個 HTML 塊,它規(guī)定了單個條目應該如何顯示访娶。然后你告訴 Angular 把這個塊當做模板,渲染列表中的每個條目觉阅。
NgFor
is a repeater directive — a way to present a list of items. You define a block of HTML that defines how a single item should be displayed. You tell Angular to use that block as a template for rendering each item in the list.
例子1:
<div *ngFor="let hero of heroes">{{hero.name}}</div>
例子2:
<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>
別忘了
ngFor
前面的星號(*
)崖疤。
賦值給 *ngFor
的字符串不是模板表達式,它是一個微語法 —— 由 Angular 自己解釋的小型語言典勇。
字符串 "let hero of heroes"
的含義:
取出 heroes
數(shù)組中的每個英雄劫哼,把它存入局部變量 hero
中,并在每次迭代時對模板 HTML 可用割笙。
Take each hero in the heroes array, store it in the local hero looping variable, and make it available to the templated HTML for each iteration.
模板輸入變量 (Template input variables)
The let
keyword before hero
creates a template input variable called hero
.
*ngFor 的索引 (index)
The index
property of the NgFor
directive context returns the zero-based index of the item in each iteration.
<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.name}}</div>
更多內(nèi)容參見 NgFor API
*ngFor with trackBy
ngFor
指令有時候會性能較差权烧,特別是在大型列表中。對一個條目的一丁點改動伤溉、移除或添加般码,都會導致級聯(lián)的 DOM 操作。
有了 trackBy
乱顾,則只有 id
發(fā)生改變才會觸發(fā)元素替換板祝。
在組件中添加方法:
trackByHeroes(index: number, hero: Hero): number { return hero.id; }
使用 trackBy
:
<div *ngFor="let hero of heroes; trackBy: trackByHeroes">
({{hero.id}}) {{hero.name}}
</div>
NgSwitch
NgSwitch can display one element from among several possible elements, based on a switch condition.
NgSwitch 由三個指令組成:
- 屬性型指令
NgSwitch
- 結(jié)構(gòu)型指令
NgSwitchCase
- 結(jié)構(gòu)型指令
NgSwitchDefault
示例:
<div [ngSwitch]="currentHero.emotion">
<happy-hero *ngSwitchCase="'happy'" [hero]="currentHero"></happy-hero>
<sad-hero *ngSwitchCase="'sad'" [hero]="currentHero"></sad-hero>
<confused-hero *ngSwitchCase="'confused'" [hero]="currentHero"></confused-hero>
<unknown-hero *ngSwitchDefault [hero]="currentHero"></unknown-hero>
</div>
NgSwitch
指令在增加或移除組件元素 (component elements) 時尤其有用。
模板引用變量 (Template reference variables) #var
模板引用變量通常是一個模版中的對 DOM 元素的一個引用走净。
A template reference variable is often a reference to a DOM element within a template.
使用井號 #
(或 ref-
)來聲明一個模板引用變量券时。The#phone
declares a phone
variable on an <input>
element.
<input #phone placeholder="phone number">
或者寫成
<input ref-phone placeholder="phone number">
你可以在模板中的任意位置引用該模板引用變量。
<input #phone placeholder="phone number">
<!-- lots of other elements -->
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>
說明:
phone
refers to the phone number<input>
box. The phone button click handler passes the *input *value to the component'scallPhone
method.
模板引用變量如何獲取自身的值伏伯?
通常橘洞,如果一個元素聲明了一個模板引用變量,那么 Angular 會將模板引用變量的值設(shè)置為該元素的值说搅。
In most cases, Angular sets the reference variable's value to the element on which it was declared.
示例:
<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm">
<div class="form-group">
<label for="name">Name
<input class="form-control" name="name" required [(ngModel)]="hero.name">
</label>
</div>
<button type="submit" [disabled]="!heroForm.form.valid">Submit</button>
</form>
<div [hidden]="!heroForm.form.valid">
{{submitMessage}}
</div>
If Angular hadn't taken it over when you imported the FormsModule
, it would be the HTMLFormElement. The heroForm
is actually a reference to an Angular NgForm directive with the ability to track the value and validity of every control in the form.
The native <form>
element doesn't have a form property. But the NgForm
directive does, which explains how you can disable the submit button if the heroForm.form.valid
is invalid and pass the entire form control tree to the parent component's onSubmit
method.
注意
模板引用變量 (template reference variable) (#phone
) 與模板輸入變量 (template input variable) (*ngFor
中的 let phone
) 并不相同炸枣。詳見結(jié)構(gòu)型指令。
輸入輸出屬性 @Input
和 @Output
綁定目標與綁定源的區(qū)別:
- 綁定的目標是在
=
左側(cè)的部分蜓堕, 源則是在=
右側(cè)的部分抛虏。 - 綁定的目標是綁定符:
[]
、()
或[()]
中的屬性或事件名套才, 源則是引號" "
中的部分或插值符號{{}}
中的部分迂猴。 - 源指令中的每個成員都會自動在綁定中可用。 不需要特別做什么背伴,就能在模板表達式或語句中訪問指令的成員沸毁。
- 訪問目標指令中的成員則受到限制峰髓。 只能綁定到那些顯式標記為輸入或輸出的屬性。
iconUrl
和 onSave
是綁定源
<img [src]="iconUrl"/>
<button (click)="onSave()">Save</button>
HeroDetailComponent.hero
是屬性綁定的目標息尺。 HeroDetailComponent.deleteRequest
是事件綁定的目標携兵。
<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)">
</hero-detail>
聲明輸入和輸出屬性
目標屬性必須被顯式的標記為輸入或輸出。
方法1:使用裝飾器 @Input()
和 @Output()
搂誉。
@Input() hero: Hero;
@Output() deleteRequest = new EventEmitter<Hero>();
方法2:通過元數(shù)據(jù)數(shù)組徐紧。
@Component({
inputs: ['hero'],
outputs: ['deleteRequest'],
})
兩種方法不可同時使用。
輸入還是輸出炭懊?
輸入屬性通常接收數(shù)據(jù)值并级。 輸出屬性暴露事件生產(chǎn)者。
Input properties usually receive data values. Output properties expose event producers.
輸入和輸出這兩個詞是從目標指令的角度來說的侮腹。
- 從
HeroDetailComponent
角度來看嘲碧,HeroDetailComponent.hero
是個輸入屬性, 因為數(shù)據(jù)流從模板綁定表達式流入那個屬性父阻。 - 從
HeroDetailComponent
角度來看愈涩,HeroDetailComponent.deleteRequest
是個輸出屬性, 因為事件從那個屬性流出加矛,流向模板綁定語句中的處理器履婉。
給輸入輸出屬性起別名
方法1:把別名傳進 @Input
/ @Output
裝飾器,就可以為屬性指定別名:
@Output('myClick') clicks = new EventEmitter<string>(); // @Output(alias) propertyName = ...
方法2:在 inputs
和 outputs
數(shù)組中為屬性指定別名荒椭。 語法(屬性名:別名)谐鼎。
@Directive({
outputs: ['clicks:myClick'] // propertyName:alias
})
模板表達式操作符
管道操作符 |
管道是一個簡單的函數(shù),它接受一個輸入值趣惠,并返回轉(zhuǎn)換結(jié)果狸棍。
<div>Title through uppercase pipe: {{title | uppercase}}</div>
管道操作符會把它左側(cè)的表達式結(jié)果傳給它右側(cè)的管道函數(shù)。
更多內(nèi)容見管道味悄。
還可以通過多個管道串聯(lián)表達式:
<!-- Pipe chaining: convert title to uppercase, then to lowercase -->
<div>
Title through a pipe chain:
{{title | uppercase | lowercase}}
</div>
還能對管道使用參數(shù):
<!-- pipe with configuration argument => "February 25, 1970" -->
<div>Birthdate: {{currentHero?.birthdate | date:'longDate'}}</div>
json
管道對調(diào)試綁定特別有用:
<div>{{currentHero | json}}</div>
輸出結(jié)果:
{ "id": 0, "name": "Hercules", "emotion": "happy",
"birthdate": "1970-02-25T08:00:00.000Z",
"url": "http://www.imdb.com/title/tt0065832/",
"rate": 325 }
安全導航操作符 ( ?. ) 和空屬性路徑
Angular 的安全導航操作符 (?.) 用來保護出現(xiàn)在屬性路徑中 null 和 undefined 值草戈。示例:
The current hero's name is {{currentHero?.name}}
說明:當
currentHero
為空時,保護視圖渲染器侍瑟,讓它免于失敗唐片。
顯示一個空 (null) 英雄的 name
示例:
The null hero's name is {{nullHero.name}}
:marked
JavaScript throws a null reference error, and so does Angular:
JavaScript 拋出了空引用錯誤,Angular 也是如此:code-example(format="nocode").
TypeError: Cannot read property 'name' of null in [null].
當 currentHero
為空的時候涨颜,應用崩潰了费韭,整個視圖都不見了。
笨重的解決辦法1:
<!--No hero, div not displayed, no error -->
<div *ngIf="nullHero">The null hero's name is {{nullHero.name}}</div>
笨重的解決辦法2:
The null hero's name is {{nullHero && nullHero.name}}
正確的解決辦法:
<!-- No hero, no problem! -->
The null hero's name is {{nullHero?.name}}
總結(jié)
透徹理解模板語法對開發(fā)至關(guān)重要庭瑰。