Angular
主要分為八大構造塊(也就是八個核心概念):模塊 (module)、組件 (component)、模板 (template)、元數據 (metadata)、數據綁定 (data binding)、指令 (directive)、服務 (service)、依賴注入 (dependency injection)。其中,最核心的一個概念就就組件。
1. 模塊 (module)
Angular
應用是模塊化的,并且 Angular
有自己的模塊系統,它被稱為 Angular
模塊或 NgModules
。
每個Angular
應用至少有一個模塊(根模塊),習慣上命名為AppModule
。
根模塊在一些小型應用中可能是唯一的模塊,大多數應用會有很多特性模塊,每個模塊都是一個內聚的代碼塊專注于某個應用領域、工作流或緊密相關的功能。
Angular
模塊(無論是根模塊還是特性模塊)都是一個帶有@NgModule
裝飾器的類。
下面是一個簡單的根模塊:
// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
其中重要的屬性是:
-
declarations
- 聲明本模塊中擁有的視圖類。Angular
有三種視圖類:組件、指令、管道; -
exports
-declarations
的子集,可用于其它模塊的組件; -
imports
- 本模塊聲明的組件模板需要的類所在的其它模塊。用來導入其他自定義模塊,第三方插件模塊; -
providers
- 服務的創建者,并加入到全局服務列表中,可用于應用任何部分; -
bootstrap
- 指定應用的主視圖(稱為根組件),它是所有其它視圖的宿主。只有根模塊才能設置bootstrap
屬性。通常在main.ts
中引導AppModule
,這樣platformBrowserDynamic().bootstrapModule(AppModule)
2. 組件 (component)
組件負責控制屏幕上的一小塊區域,我們稱之為視圖。
下面是一個組件的簡單例子:
// src/app/hero-list.component.ts
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; }
}
3. 模板 (template)
模板就是HTML文件,但是不是標準的HTML文件,它使用了一些模板語法,模板語法使模板有了自己的邏輯關系,并能夠實現和組件的簡單數據交互。
下面是一個簡單的模板:
// src/app/hero-list.component.html
<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>
<hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
通常使用ng g component my-component
命令產生一個組件包含四個文件:
my-component.css // 樣式文件
my-component.thml // 模板
my-component.spec.ts // 測試文件
my-component.ts // 這是組件? 通常我們認為這四個文件組成一個組件
4. 元數據 (metadata)
元數據告訴你如何處理一個類。
其實,在Angular
中每個組件只是一個類,但是我們可以通過裝飾器來附加元數據告訴Angular
這是一個組件。
下面就是HeroListComponent
的一些元數據。
// src/app/hero-list.component.ts (metadata)
@Component({ // @Component 將后面的 HeroListComponent 類標記為一個組件
selector: 'hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})
export class HeroListComponent implements OnInit {
/* . . . */
}
@Component
裝飾器能接受一個配置對象, Angular 會基于這些信息創建和展示組件及其視圖。
@Component
的配置項包括:
-
selector
:CSS
選擇器,它告訴Angular
在父級HTML
中查找<hero-list>
標簽,創建并插入該組件。 例如,如果應用的HTML
包含<hero-list></hero-list>
,Angular
就會把HeroListComponent
的一個實例插入到這個標簽中; -
templateUrl
:組件HTML
模板的模塊相對地址; -
providers
- 組件所需服務的依賴注入提供商數組。 這是在告訴Angular
:該組件的構造函數需要一個HeroService
服務,這樣組件就可以從服務中獲得英雄數據。
到這里你應該可以明白:模板、元數據和組件共同描繪出這個視圖。
5. 數據綁定 (databinding)
數據綁定是Angular
中最常用的數據處理模式。數據綁定在模板與對應組件的交互中扮演了重要的角色,在父組件與子組件的通訊中也同樣重要。
下面是一個簡單的例子:
// src/app/hero-list.component.html
<li>{{hero.name}}</li>
<hero-detail [hero]="selectedHero"></hero-detail>
<li (click)="selectHero(hero)"></li>
-
{{hero.name}}
插值表達式在<li>
標簽中顯示組件的hero.name
屬性的值; -
[hero]
屬性綁定把父組件HeroListComponent
的selectedHero
的值傳到子組件HeroDetailComponent
的hero
屬性中; -
(click)
事件綁定在用戶點擊英雄的名字時調用組件的selectHero
方法。
Angular
默認沒有雙向綁定,但是,官方推薦這樣來實現雙向綁定:
<input [(ngModel)]="hero.name">
Angular
在每個 JavaScript
事件循環中處理所有的數據綁定,它會從組件樹的根部開始,遞歸處理全部子組件。
6. 指令 (directive)
由于Angular
模板是動態的,所以你需要通過指令實現對DOM
的轉換。(組件是一個帶模板的指令;@Component
裝飾器實際上就是一個@Directive
裝飾器,只是擴展了一些面向模板的特性。 )
指令分為兩種:結構指令、屬性指令。
a. 結構指令: 通過在 DOM 中添加、移除和替換元素來修改布局。
下面是一個簡單的內置結構指令的例子:
<!-- src/app/hero-list.component.html (structural) -->
<li *ngFor="let hero of heroes"></li>
<hero-detail *ngIf="selectedHero"></hero-detail>
-
*ngFor
告訴 Angular 為heroes
列表中的每個英雄生成一個<li>
標簽。 -
*ngIf
表示只有在選擇的英雄存在時,才會包含HeroDetail
組件。
b. 屬性指令:修改一個現有元素的外觀或行為。
簡單例子:
<!-- src/app/hero-detail.component.html -->
<input [(ngModel)]="hero.name">
7. 服務 (service)
服務是一個廣義范疇,包括:值、函數,或應用所需的特性。幾乎任何東西都可以是一個服務。 典型的服務是一個類,具有專注的、明確的用途。它應該做一件特定的事情,并把它做好。
例如:
- 日志服務
- 數據服務
- 消息總線
- 稅款計算器
- 應用程序配置
組件類應保持精簡。組件本身不從服務器獲得數據、不進行驗證輸入,也不直接往控制臺寫日志。 它們把這些任務委托給服務。所以說服務是跑腿的,服務一般用來處理業務邏輯,被注入在組件當中,服務是全局單例的。也就是說注入到所有組件中的服務是同一個。
一個簡單的例子:
// src/app/hero.service.ts
export class HeroService {
private heroes: Hero[] = [];
constructor(
private backend: BackendService,
private logger: Logger) { }
getHeroes() {
this.backend.getAll(Hero).then( (heroes: Hero[]) => {
this.logger.log(`Fetched ${heroes.length} heroes.`);
this.heroes.push(...heroes); // fill cache
});
return this.heroes;
}
}
8. 依賴注入 (dependency injection)
“依賴注入”是提供類的新實例的一種方式,還負責處理好類所需的全部依賴。大多數依賴都是服務。 Angular
使用依賴注入來提供新組件以及組件所需的服務。
來看注入方式:
// src/app/hero-list.component.ts
constructor(private service: HeroService) { }
注入器維護了一個服務實例的容器,存放著以前創建的實例。 如果所請求的服務實例不在容器中,注入器就會創建一個服務實例,并且添加到容器中,然后把這個服務返回給 Angular。 當所有請求的服務都被解析完并返回時,Angular 會以這些服務為參數去調用組件的構造函數。 這就是依賴注入 。
通常我們將服務聲明在根模塊,以便在整個應用中使用這個服務。
// src/app/app.module.ts
providers: [
BackendService,
HeroService,
Logger
],
也可以在其他組件中聲明服務,那么這個服務只能用于當前組件。把它注冊在組件級表示該組件的每一個新實例都會有一個服務的新實例。
// src/app/hero-list.component.ts
@Component({
selector: 'hero-list',
templateUrl: './hero-list.component.html',
providers: [ HeroService ]
})