組件控制屏幕上被稱為視圖的一小片區(qū)域。比如,教程中的下列視圖都是由一個(gè)個(gè)組件所定義和控制的:
你在類中定義組件的應(yīng)用邏輯,為視圖提供支持。 組件通過一些由屬性和方法組成的 API 與視圖交互。
比如,HeroListComponent 中有一個(gè) 名為 heroes
的屬性,它儲(chǔ)存著一個(gè)數(shù)組的英雄數(shù)據(jù)。 HeroListComponent 還有一個(gè) selectHero()
方法,當(dāng)用戶從列表中選擇一個(gè)英雄時(shí),它會(huì)設(shè)置 selectedHero
屬性的值。 該組件會(huì)從服務(wù)獲取英雄列表,它是一個(gè) TypeScript 的構(gòu)造器參數(shù)型屬性。本服務(wù)通過依賴注入系統(tǒng)提供給該組件。
export class HeroListComponent implements OnInit {
heroes: Hero[];
selectedHero: Hero;
constructor(private service: HeroService) { }
ngOnInit() {
this.heroes = this.service.getHeroes();
}
selectHero(hero: Hero) { this.selectedHero = hero; }
}
當(dāng)用戶在應(yīng)用中穿行時(shí),Angular 就會(huì)創(chuàng)建、更新、銷毀一些組件。 你的應(yīng)用可以通過一些可選的生命周期鉤子(比如 ngOnInit()
)來在每個(gè)特定的時(shí)機(jī)采取行動(dòng)。
@Component 裝飾器會(huì)指出緊隨其后的那個(gè)類是個(gè)組件類,并為其指定元數(shù)據(jù)。 在下面的范例代碼中,你可以看到 HeroListComponent 只是一個(gè)普通類,完全沒有 Angular 特有的標(biāo)記或語法。 直到給它加上了 @Component 裝飾器,它才變成了組件。
組件的元數(shù)據(jù)告訴 Angular 到哪里獲取它需要的主要構(gòu)造塊,以創(chuàng)建和展示這個(gè)組件及其視圖。 具體來說,它把一個(gè)模板(無論是直接內(nèi)聯(lián)在代碼中還是引用的外部文件)和該組件關(guān)聯(lián)起來。 該組件及其模板,共同描述了一個(gè)視圖。
除了包含或指向模板之外,@Component 的元數(shù)據(jù)還會(huì)配置要如何在 HTML 中引用該組件,以及該組件需要哪些服務(wù)等等。
下面的例子中就是 HeroListComponent 的基礎(chǔ)元數(shù)據(jù):
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
export class HeroListComponent implements OnInit {
/* . . . */
}
這個(gè)例子展示了一些最常用的 @Component 配置選項(xiàng):
你要通過組件的配套模板來定義其視圖。模板就是一種 HTML,它會(huì)告訴 Angular 如何渲染該組件。
視圖通常會(huì)分層次進(jìn)行組織,讓你能以 UI 分區(qū)或頁(yè)面為單位進(jìn)行修改、顯示或隱藏。 與組件直接關(guān)聯(lián)的模板會(huì)定義該組件的宿主視圖。該組件還可以定義一個(gè)帶層次結(jié)構(gòu)的視圖,它包含一些內(nèi)嵌的視圖作為其它組件的宿主。
帶層次結(jié)構(gòu)的視圖可以包含同一模塊(NgModule)中組件的視圖,也可以(而且經(jīng)常會(huì))包含其它模塊中定義的組件的視圖。
模板很像標(biāo)準(zhǔn)的 HTML,但是它還包含 Angular 的模板語法,這些模板語法可以根據(jù)你的應(yīng)用邏輯、應(yīng)用狀態(tài)和 DOM 數(shù)據(jù)來修改這些 HTML。 你的模板可以使用數(shù)據(jù)綁定來協(xié)調(diào)應(yīng)用和 DOM 中的數(shù)據(jù),使用管道在顯示出來之前對(duì)其進(jìn)行轉(zhuǎn)換,使用指令來把程序邏輯應(yīng)用到要顯示的內(nèi)容上。
比如,下面是本教程中 HeroListComponent 的模板:
<h2>Hero List</h2>
<p><i>Pick a hero from the list</i></p>
<ul>
<li *ngFor="let hero of heroes" (click)="selectHero(hero)">
{{hero.name}}
</li>
</ul>
<app-hero-detail *ngIf="selectedHero" [hero]="selectedHero"></app-hero-detail>
這個(gè)模板使用了典型的 HTML 元素,比如 <h2>
和 <p>
,還包括一些 Angular 的模板語法元素,如 *ngFor
,{{hero.name}}
,click
、[hero]
和 <app-hero-detail>
。這些模板語法元素告訴 Angular 該如何根據(jù)程序邏輯和數(shù)據(jù)在屏幕上渲染 HTML。
*ngFor
指令告訴 Angular 在一個(gè)列表上進(jìn)行迭代。{{hero.name}}
、(click)
和 [hero]
把程序數(shù)據(jù)綁定到及綁定回 DOM,以響應(yīng)用戶的輸入。更多內(nèi)容參見稍后的數(shù)據(jù)綁定部分。<app-hero-detail>
標(biāo)簽是一個(gè)代表新組件 HeroDetailComponent 的元素。 HeroDetailComponent(代碼略)定義了 HeroListComponent 的英雄詳情子視圖。 注意觀察像這樣的自定義組件是如何與原生 HTML 元素?zé)o縫的混合在一起的。如果沒有框架,你就要自己負(fù)責(zé)把數(shù)據(jù)值推送到 HTML 控件中,并把來自用戶的響應(yīng)轉(zhuǎn)換成動(dòng)作和對(duì)值的更新。 手動(dòng)寫這種數(shù)據(jù)推拉邏輯會(huì)很枯燥、容易出錯(cuò),難以閱讀 —— 有前端 JavaScript 開發(fā)經(jīng)驗(yàn)的程序員一定深有體會(huì)。
Angular 支持雙向數(shù)據(jù)綁定,這是一種對(duì)模板中的各個(gè)部件與組件中的各個(gè)部件進(jìn)行協(xié)調(diào)的機(jī)制。 往模板 HTML 中添加綁定標(biāo)記可以告訴 Angular 該如何連接它們。
下圖顯示了數(shù)據(jù)綁定標(biāo)記的四種形式。每種形式都有一個(gè)方向 —— 從組件到 DOM、從 DOM 到組件或雙向。
這個(gè)來自 HeroListComponent 模板中的例子展示了其中的三種形式:
<li>{{hero.name}}</li>
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
<li (click)="selectHero(hero)"></li>
雙向數(shù)據(jù)綁定(主要用于模板驅(qū)動(dòng)表單中),它會(huì)把屬性綁定和事件綁定組合成一種單獨(dú)的寫法。下面這個(gè)來自 HeroDetailComponent 模板中的例子通過 ngModel 指令使用了雙向數(shù)據(jù)綁定:
<input [(ngModel)]="hero.name">
在雙向綁定中,數(shù)據(jù)屬性值通過屬性綁定從組件流到輸入框。用戶的修改通過事件綁定流回組件,把屬性值設(shè)置為最新的值。
Angular 在每個(gè) JavaScript 事件循環(huán)中處理所有的數(shù)據(jù)綁定,它會(huì)從組件樹的根部開始,遞歸處理全部子組件。
數(shù)據(jù)綁定在模板及其組件之間的通訊中扮演了非常重要的角色,它對(duì)于父組件和子組件之間的通訊也同樣重要。
Angular 的管道可以讓你在模板中聲明顯示值的轉(zhuǎn)換邏輯。 帶有 @Pipe 裝飾器的類中會(huì)定義一個(gè)轉(zhuǎn)換函數(shù),用來把輸入值轉(zhuǎn)換成供視圖顯示用的輸出值。
Angular 自帶了很多管道,比如 date
管道和 currency
管道,完整的列表參見 Pipes API 列表。你也可以自己定義一些新管道。
要在 HTML 模板中指定值的轉(zhuǎn)換方式,請(qǐng)使用 管道操作符 (|
)。
{{interpolated_value | pipe_name}}
你可以把管道串聯(lián)起來,把一個(gè)管道函數(shù)的輸出送給另一個(gè)管道函數(shù)進(jìn)行轉(zhuǎn)換。 管道還能接收一些參數(shù),來控制它該如何進(jìn)行轉(zhuǎn)換。比如,你可以把要使用的日期格式傳給 date
管道:
<!-- Default format: output 'Jun 15, 2015'-->
<p>Today is {{today | date}}</p>
<!-- fullDate format: output 'Monday, June 15, 2015'-->
<p>The date is {{today | date:'fullDate'}}</p>
<!-- shortTime format: output '9:43 AM'-->
<p>The time is {{today | date:'shortTime'}}</p>
Angular 的模板是動(dòng)態(tài)的。當(dāng) Angular 渲染它們的時(shí)候,會(huì)根據(jù)指令給出的指示對(duì) DOM 進(jìn)行轉(zhuǎn)換。 指令就是一個(gè)帶有 @Directive()
裝飾器的類。
組件從技術(shù)角度上說就是一個(gè)指令,但是由于組件對(duì) Angular 應(yīng)用來說非常獨(dú)特、非常重要,因此 Angular 專門定義了 @Component()
裝飾器,它使用一些面向模板的特性擴(kuò)展了 @Directive()
裝飾器。
除組件外,還有兩種指令:結(jié)構(gòu)型指令和屬性型指令。 Angular 本身定義了一系列這兩種類型的指令,你也可以使用 @Directive()
裝飾器來定義自己的指令。
像組件一樣,指令的元數(shù)據(jù)把它所裝飾的指令類和一個(gè) selector
關(guān)聯(lián)起來,selector
用來把該指令插入到 HTML 中。 在模板中,指令通常作為屬性出現(xiàn)在元素標(biāo)簽上,可能僅僅作為名字出現(xiàn),也可能作為賦值目標(biāo)或綁定目標(biāo)出現(xiàn)。
1. 結(jié)構(gòu)型指令
結(jié)構(gòu)型指令通過添加、移除或替換 DOM 元素來修改布局。 這個(gè)范例模板使用了兩個(gè)內(nèi)置的結(jié)構(gòu)型指令來為要渲染的視圖添加程序邏輯:
<li *ngFor="let hero of heroes"></li>
<app-hero-detail *ngIf="selectedHero"></app-hero-detail>
*ngFor
是一個(gè)迭代器,它要求 Angular 為 heroes 列表中的每個(gè)英雄渲染出一個(gè) <li>
。*ngIf
是個(gè)條件語句,只有當(dāng)選中的英雄存在時(shí),它才會(huì)包含 HeroDetail 組件。2. 屬性型指令
屬性型指令會(huì)修改現(xiàn)有元素的外觀或行為。 在模板中,它們看起來就像普通的 HTML 屬性一樣,因此得名“屬性型指令”。
ngModel 指令就是屬性型指令的一個(gè)例子,它實(shí)現(xiàn)了雙向數(shù)據(jù)綁定。 ngModel 修改現(xiàn)有元素(一般是 <input>
)的行為:設(shè)置其顯示屬性值,并響應(yīng) change 事件。
<input [(ngModel)]="hero.name">
注:
- Angular 還有很多預(yù)定義指令,有些修改布局結(jié)構(gòu)(比如ngSwitch
),有些修改 DOM 元素和組件的樣子(比如ngStyle
和ngClass
)。
- 參考 [Angular9 結(jié)構(gòu)型指令]() 和 [Angular9 屬性型指令]() 以了解 Angular 兩種指令類型。
更多建議: