指令是為 Angular 應(yīng)用程序中的元素添加額外行為的類。使用 Angular 的內(nèi)置指令,你可以管理表單、列表、樣式以及要讓用戶看到的任何內(nèi)容。
要查看包含本指南中代碼的可工作范例,請(qǐng)參閱現(xiàn)場(chǎng)演練 / 下載范例。
Angular 指令的不同類型如下:
本指南涵蓋了內(nèi)置的屬性型指令和結(jié)構(gòu)型指令。
屬性型指令會(huì)監(jiān)聽并修改其它 HTML 元素和組件的行為、Attribute 和 Property。
許多 NgModule(例如 ?RouterModule
?和 ?FormsModule
?都定義了自己的屬性型指令。最常見的屬性型指令如下:
NgClass
?—— 添加和刪除一組 CSS 類。NgStyle
?—— 添加和刪除一組 HTML 樣式。NgModel
?—— 將數(shù)據(jù)雙向綁定添加到 HTML 表單元素。內(nèi)置指令只會(huì)使用公開 API。它們不會(huì)訪問任何無法被其它指令訪問的私有 API。
用 ?ngClass
?同時(shí)添加或刪除多個(gè) CSS 類。
要添加或刪除單個(gè)類,請(qǐng)使用類綁定而不是 ?NgClass
?。
在要設(shè)置樣式的元素上,添加 ?[ngClass]
? 并將其設(shè)置為等于某個(gè)表達(dá)式。在這里,是在 ?app.component.ts
? 中將 ?isSpecial
?設(shè)置為布爾值 ?true
?。因?yàn)?nbsp;?isSpecial
?為 ?true
?,所以 ?ngClass
?就會(huì)把 ?special
?類應(yīng)用于此 ?<div>
? 上。
<!-- toggle the "special" class on/off with a property -->
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
NgClass
?與方法一起使用,請(qǐng)將方法添加到組件類中。在下面的示例中,?setCurrentClasses()
? 使用一個(gè)對(duì)象來設(shè)置屬性 ?currentClasses
?,該對(duì)象根據(jù)另外三個(gè)組件屬性為 ?true
?或 ?false
?來添加或刪除三個(gè) CSS 類。該對(duì)象的每個(gè)鍵(key)都是一個(gè) CSS 類名。如果鍵為 ?true
?,則 ?ngClass
?添加該類。如果鍵為 ?false
?,則 ?ngClass
?刪除該類。
currentClasses: Record<string, boolean> = {};
/* . . . */
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>
在這個(gè)例子中,Angular 會(huì)在初始化以及發(fā)生更改的情況下應(yīng)用這些類。完整的示例會(huì)在 ngOnInit()
中進(jìn)行初始化以及通過單擊按鈕更改相關(guān)屬性時(shí)調(diào)用 setCurrentClasses()
。這些步驟對(duì)于實(shí)現(xiàn) ngClass
不是必需的。有關(guān)更多信息,請(qǐng)參見
app.component.ts
和 app.component.html
。
可以用 ?NgStyle
?根據(jù)組件的狀態(tài)同時(shí)設(shè)置多個(gè)內(nèi)聯(lián)樣式。
NgStyle
?,請(qǐng)向組件類添加一個(gè)方法。在下面的例子中,?setCurrentStyles()
? 方法基于該組件另外三個(gè)屬性的狀態(tài),用一個(gè)定義了三個(gè)樣式的對(duì)象設(shè)置了 ?currentStyles
?屬性。
currentStyles: Record<string, string> = {};
/* . . . */
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'
};
}
ngStyle
?屬性綁定到 ?currentStyles
?。<div [ngStyle]="currentStyles">
This div is initially italic, normal weight, and extra large (24px).
</div>
在這個(gè)例子中,Angular 會(huì)在初始化以及發(fā)生更改的情況下應(yīng)用這些類。完整的示例會(huì)在 ngOnInit()
中進(jìn)行初始化以及通過單擊按鈕更改相關(guān)屬性時(shí)調(diào)用 setCurrentClasses()
。這些步驟對(duì)于實(shí)現(xiàn) ngClass
不是必需的。有關(guān)更多信息,請(qǐng)參見
app.component.ts
和 app.component.html
。
可以用 ?NgModel
?指令顯示數(shù)據(jù)屬性,并在用戶進(jìn)行更改時(shí)更新該屬性。
FormsModule
?,并將其添加到 NgModule 的 ?imports
?列表中。import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular
/* . . . */
@NgModule({
/* . . . */
imports: [
BrowserModule,
FormsModule // <--- import into the NgModule
],
/* . . . */
})
export class AppModule { }
<form>
? 元素上添加 ?[(ngModel)]
? 綁定,并將其設(shè)置為等于此屬性,這里是 ?name
?。<label for="example-ngModel">[(ngModel)]:</label>
<input [(ngModel)]="currentItem.name" id="example-ngModel">
此 ?[(ngModel)]
? 語法只能設(shè)置數(shù)據(jù)綁定屬性。
要自定義配置,你可以編寫可展開的表單,該表單將屬性綁定和事件綁定分開。使用屬性綁定來設(shè)置屬性,并使用事件綁定來響應(yīng)更改。以下示例將 ?<input>
? 值更改為大寫:
<input [ngModel]="currentItem.name" (ngModelChange)="setUppercaseName($event)" id="example-uppercase">
這里是所有這些變體的動(dòng)畫,包括這個(gè)大寫轉(zhuǎn)換的版本:
?NgModel
?指令適用于?ControlValueAccessor
?支持的元素。Angular 為所有基本 HTML 表單元素提供了值訪問器。
要將 ?[(ngModel)]
? 應(yīng)用于非表單型內(nèi)置元素或第三方自定義組件,必須編寫一個(gè)值訪問器。
編寫 Angular 組件時(shí),如果根據(jù) Angular 的雙向綁定語法命名 value 和 event 屬性,則不需要用值訪問器(ControlValueAccessor)或 ?
NgModel
?。
結(jié)構(gòu)型指令的職責(zé)是 HTML 布局。 它們塑造或重塑 DOM 的結(jié)構(gòu),這通常是通過添加、移除和操縱它們所附加到的宿主元素來實(shí)現(xiàn)的。
本節(jié)會(huì)介紹最常見的內(nèi)置結(jié)構(gòu)型指令:
NgIf
?—— 從模板中創(chuàng)建或銷毀子視圖。NgFor
?—— 為列表中的每個(gè)條目重復(fù)渲染一個(gè)節(jié)點(diǎn)。NgSwitch
?—— 一組在備用視圖之間切換的指令。可以將 ?NgIf
?指令應(yīng)用于宿主元素來添加或刪除元素。
如果 ?NgIf
?為 ?false
?,則 Angular 將從 DOM 中移除一個(gè)元素及其后代。然后,Angular 會(huì)銷毀其組件,從而釋放內(nèi)存和資源。
要添加或刪除元素,請(qǐng)?jiān)谝韵率纠?nbsp;?*ngIf
? 綁定到條件表達(dá)式,例如 ?isActive
?
<app-item-detail *ngIf="isActive" [item]="item"></app-item-detail>
當(dāng) ?isActive
?表達(dá)式返回真值時(shí),?NgIf
?會(huì)把 ?ItemDetailComponent
?添加到 DOM 中。當(dāng)表達(dá)式為假值時(shí),?NgIf
?會(huì)從 DOM 中刪除 ?ItemDetailComponent
?并銷毀該組件及其所有子組件。
默認(rèn)情況下,?NgIf
?會(huì)阻止顯示已綁定到空值的元素。
要使用 ?NgIf
?保護(hù) ?<div>
?,請(qǐng)將 ?*ngIf="yourProperty"
? 添加到此 ?<div>
?。在下面的例子中,?currentCustomer
?名字出現(xiàn)了,是因?yàn)榇_實(shí)存在一個(gè) ?currentCustomer
?。
<div *ngIf="currentCustomer">Hello, {{currentCustomer.name}}</div>
但是,如果該屬性為 ?null
?,則 Angular 就不會(huì)顯示 ?<div>
?。在這個(gè)例子中,Angular 就不會(huì)顯示 ?nullCustomer
?,因?yàn)樗鼮?nbsp;?null
?。
<div *ngIf="nullCustomer">Hello, <span>{{nullCustomer}}</span></div>
可以用 ?NgFor
?來指令顯示條目列表。
let item of items
? 賦值給 ?*ngFor
?。<div *ngFor="let item of items">{{item.name}}</div>
字符串 ?"let item of items"
? 會(huì)指示 Angular 執(zhí)行以下操作:
items
?中的每個(gè)條目存儲(chǔ)在局部循環(huán)變量 ?item
?中"let item of items"
? 轉(zhuǎn)換為環(huán)繞宿主元素的 ?<ng-template>
?item
?復(fù)寫這個(gè) ?<ng-template>
?要復(fù)寫某個(gè)組件元素,請(qǐng)將 ?*ngFor
? 應(yīng)用于其選擇器。在以下示例中,選擇器為 ?<app-item-detail>
?。
<app-item-detail *ngFor="let item of items" [item]="item"></app-item-detail>
你可以在以下位置引用模板輸入變量,例如 ?item
?:
ngFor
?的宿主元素中以下示例首先在插值中引用 ?item
?,然后將它通過綁定傳遞給 ?<app-item-detail>
? 組件的 ?item
?屬性。
<div *ngFor="let item of items">{{item.name}}</div>
<!-- . . . -->
<app-item-detail *ngFor="let item of items" [item]="item"></app-item-detail>
可以獲取 ?*ngFor
? 的 ?index
?,并在模板中使用它。
在 ?*ngFor
? 中,添加一個(gè)分號(hào)和 ?let i=index
? 簡寫形式。下面的例子中把 ?index
?取到一個(gè)名為 ?i
? 的變量中,并將其與條目名稱一起顯示。
<div *ngFor="let item of items; let i=index">{{i + 1}} - {{item.name}}</div>
?NgFor
?指令上下文的 ?index
?屬性在每次迭代中都會(huì)返回該條目的從零開始的索引號(hào)。
Angular 會(huì)將此指令轉(zhuǎn)換為 ?<ng-template>
?,然后反復(fù)使用此模板為列表中的每個(gè) ?item
?創(chuàng)建一組新的元素和綁定。
要在特定條件為 true 時(shí)復(fù)寫 HTML 塊,請(qǐng)將 ?*ngIf
? 放在 ?*ngFor
? 元素的容器元素上。它們之一或兩者都可以是 ?<ng-container>
?,這樣你就不必引入額外的 HTML 層次了。
由于結(jié)構(gòu)型指令會(huì)在 DOM 中添加和刪除節(jié)點(diǎn),因此每個(gè)元素只能應(yīng)用一個(gè)結(jié)構(gòu)型指令。
通過跟蹤對(duì)條目列表的更改,可以減少應(yīng)用程序?qū)Ψ?wù)器的調(diào)用次數(shù)。使用 ?*ngFor
? 的 ?trackBy
?屬性,Angular 只能更改和重新渲染已更改的條目,而不必重新加載整個(gè)條目列表。
NgFor
?應(yīng)該跟蹤的值。這個(gè)例子中,該值是英雄的 ?id
?。如果瀏覽器已經(jīng)渲染過此 ?id
?,Angular 就會(huì)跟蹤它,而不會(huì)重新向服務(wù)器查詢相同的 ?id
?。trackByItems(index: number, item: Item): number { return item.id; }
trackBy
?設(shè)置為 ?trackByItems()
? 方法。<div *ngFor="let item of items; trackBy: trackByItems">
({{item.id}}) {{item.name}}
</div>
更改這些 ID 會(huì)使用新的 ?item.id
? 創(chuàng)建新的條目。在下面的 ?trackBy
?效果演示中,Reset items 會(huì)創(chuàng)建一些具有和以前相同的 ?item.id
? 的新條目。
trackBy
?,這些按鈕都會(huì)觸發(fā)完全的 DOM 元素替換。trackBy
?,則只有修改了 ?id
?的按鈕才會(huì)觸發(fā)元素替換。
Angular 的 ?<ng-container>
? 是一個(gè)分組元素,它不會(huì)干擾樣式或布局,因?yàn)?nbsp;Angular 不會(huì)將其放置在 DOM 中。
當(dāng)沒有單個(gè)元素承載指令時(shí),可以使用 ?<ng-container>
?。
這是使用 ?<ng-container>
? 的條件化段落。
<p>
I turned the corner
<ng-container *ngIf="hero">
and saw {{hero.name}}. I waved
</ng-container>
and continued on my way.
</p>
FormsModule
?中導(dǎo)入 ?ngModel
?指令。FormsModule
?添加到相關(guān) Angular 模塊的 imports 部分。<option>
?,請(qǐng)將 ?<option>
? 包裹在 ?<ng-container>
? 中。<div>
Pick your favorite hero
(<label><input type="checkbox" checked (change)="showSad = !showSad">show sad</label>)
</div>
<select [(ngModel)]="hero">
<ng-container *ngFor="let h of heroes">
<ng-container *ngIf="showSad || h.emotion !== 'sad'">
<option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
</ng-container>
</ng-container>
</select>
就像 JavaScript 的 ?switch
?語句一樣。?NgSwitch
?會(huì)根據(jù)切換條件顯示幾個(gè)可能的元素中的一個(gè)。Angular 只會(huì)將選定的元素放入 DOM。
?NgSwitch
?是一組指令(共三個(gè)):
NgSwitch
?—— 一個(gè)屬性型指令,它更改其伴生指令的行為。NgSwitchCase
?—— 結(jié)構(gòu)型指令,當(dāng)其綁定值等于開關(guān)值時(shí)將其元素添加到 DOM 中,而在其不等于開關(guān)值時(shí)將其綁定值移除。NgSwitchDefault
?—— 結(jié)構(gòu)型指令,當(dāng)沒有選中的 ?NgSwitchCase
?時(shí),將其宿主元素添加到 DOM 中。<div>
?)上,把 ?[ngSwitch]
? 綁定到一個(gè)返回開關(guān)值的表達(dá)式(例如 ?feature
?)。盡管這個(gè)例子中 ?feature
?值是字符串,但此開關(guān)值可以是任何類型。*ngSwitchCase
? 和 ?*ngSwitchDefault
?。<div [ngSwitch]="currentItem.feature">
<app-stout-item *ngSwitchCase="'stout'" [item]="currentItem"></app-stout-item>
<app-device-item *ngSwitchCase="'slim'" [item]="currentItem"></app-device-item>
<app-lost-item *ngSwitchCase="'vintage'" [item]="currentItem"></app-lost-item>
<app-best-item *ngSwitchCase="'bright'" [item]="currentItem"></app-best-item>
<!-- . . . -->
<app-unknown-item *ngSwitchDefault [item]="currentItem"></app-unknown-item>
</div>
currentItem
?以便可以在 ?[ngSwitch]
? 表達(dá)式中使用它。currentItem!: Item;
item
?,該屬性會(huì)綁定到父組件的 ?currentItem
?。以下兩個(gè)片段顯示了父組件和其中一個(gè)子組件。其他子組件與 ?StoutItemComponent
?中的相同。export class StoutItemComponent {
@Input() item!: Item;
}
Switch 指令也同樣適用于內(nèi)置 HTML 元素和 Web Component。 比如,你可以像下面的例子中一樣把 ?<app-best-item>
? 分支替換為 ?<div>
?。
<div *ngSwitchCase="'bright'"> Are you as bright as {{currentItem.name}}?</div>
更多建議: