學(xué)習(xí)資料來自 Angular.cn 與 Angular.io。
雖然官方文檔很全面,但小鐳覺得官方文檔寫的很雜亂,重復(fù)的內(nèi)容很多。小鐳沒有到 Github 上細(xì)看,估計(jì)是很多人分別寫不同的部分最后匯總起來的。
該筆記將對(duì)官方文檔中的教程、指南、烹飪書等諸多內(nèi)容按照小鐳自己的思路和學(xué)習(xí)路線重新進(jìn)行整理,分享給每一個(gè)希望自學(xué) Angular 的朋友。
Angular 與 AngularJS 的差別非常大,無論是語法還是編寫方式。怪不得名字改了,讓小鐳感覺完全是兩個(gè)東西。
重新學(xué)吧 ! Angular 值得我們這樣做,因?yàn)樗浅:糜谩?/em>
快速起步
Angular 應(yīng)用由組件(components)組成。
組件包含:
- HTML 模版(template)
- 組件類(component class)
組件類控制視圖。
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1>`
})
export class AppComponent { name = 'Angular'; }
每個(gè)組件都以 @Component 裝飾器函數(shù)(decorator function)開始,它接受一個(gè)元數(shù)據(jù)對(duì)象(metadata object)參數(shù)。該元數(shù)據(jù)對(duì)象描述了 HTML 模板和組件類是如何一起工作的。
上例說明:
-
@Component({...})中的{}為元數(shù)據(jù)對(duì)象參數(shù) -
selector屬性為 Angular 指定了在index.html中的自定義<my-app>標(biāo)簽里顯示該組件 -
template屬性定義了<h1>標(biāo)題里的一條消息 -
{{name}}為插值綁定表達(dá)式(interpolation binding expression)
有關(guān)插值綁定表達(dá)式的更多知識(shí)見 顯示數(shù)據(jù)
頁(yè)面樣例:index.html
<body>
<my-app>Loading AppComponent content here ...</my-app>
<body>
Angular 使用 TypeScript(基于效率考慮)。也可以使用 JavaScript,見指南。
教程: 英雄指南
英雄編輯器
該部分的在線例子。
準(zhǔn)備工作-本地環(huán)境搭建
參看頁(yè)面 搭建本地開發(fā)環(huán)境 并按照步驟操作即可。
安裝完種子包后,可參考 搭建剖析 了解文件夾內(nèi)各個(gè)文件的用途。
開始
小鐳直接利用上述種子包文件開始構(gòu)建項(xiàng)目。
保持應(yīng)用不斷轉(zhuǎn)譯和運(yùn)行
在命令行窗口中輸入:
npm start
該命令用途:
- 在"監(jiān)聽"模式下運(yùn)行編譯器
- 啟動(dòng)開發(fā)服務(wù)器
- 在瀏覽器中啟動(dòng)應(yīng)用
- 使應(yīng)用在后續(xù)構(gòu)建過程中能夠持續(xù)運(yùn)行
顯示英雄
小鐳的本地環(huán)境無法正常工作,應(yīng)用未成功加載,<my-app> 內(nèi)容未更新。
瀏覽器 Console 報(bào)錯(cuò)如下:
Error: (SystemJS) Block-scoped declarations
(let, const, function, class) not yet supported outside strict mode
看來是 systemjs-angular-loader.js 文件出了問題。
在該文件首行加入如下代碼:
"use strict";
OK,問題解決。不過瀏覽器 Console 又有提示:
Angular is running in the development mode.
Call enableProdMode() to enable the production mode.
說明開發(fā)模式和生產(chǎn)模式是通過 enableProdMode() 方法來控制切換的。
那么如何使用這個(gè)方法呢?教程中居然沒有順帶提一句,唉……
其實(shí)我們只需要在 main.ts 中使用它就可以了,代碼如下:
import {enableProdMode} from '@angular/core';
enableProdMode();
此處使用該方法前一定要先 import 它,就像 platformBrowserDynamic() 方法一樣。
注意,上述代碼要放在 platformBrowserDynamic() 方法前使用,否則會(huì)報(bào)錯(cuò):
Error: (SystemJS) Cannot enable prod mode after platform setup.
OK,現(xiàn)在已經(jīng)知道如何切換工作模式了,現(xiàn)階段還是使用開發(fā)模式吧。
Hero 對(duì)象
此處需要注意的是代碼的位置。
如果 Hero 類插在了 @Component 與 AppComponent 的中間,則會(huì)報(bào)錯(cuò):
Error: (SystemJS) Unexpected value 'AppComponent' declared by the module 'AppModule'.
Please add a @Pipe/@Directive/@Component annotation.
@Component 與 AppComponent 一定要連起來寫。所以應(yīng)該先讀一下 API 手冊(cè)中的相關(guān)內(nèi)容了解語法規(guī)則。
使用多行模板字符串添加更多 HTML
使用反引號(hào)``的模板字符串(也稱做——模板字面量,Template literals)。
樣例 app.component.ts (AppComponent's template)
template: `
<h1>{{title}}</h1>
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div><label>name: </label>{{hero.name}}</div>
`
編輯英雄名字
雙向綁定
1. 修改 app.module.ts 文件,導(dǎo)入 FormsModule 包:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
FormsModule // <-- import the FormsModule before binding with [(ngModel)]
],
declarations: [
AppComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
FormsModule 包中包含了 ngModel 指令,它用于雙向綁定。
2. 更新模版
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name">
</div>
該部分的在線例子鏈接。
主從結(jié)構(gòu)
該部分的在線例子。
*ngFor 語法
<li *ngFor="let hero of heroes">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
關(guān)于
*ngFor和模板輸入變量的更多知識(shí),見顯示數(shù)據(jù)和模板語法。
事件綁定例句
<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
...
</li>
*ngIf 語法
<div *ngIf="selectedHero">
...
</div>
*ngIf和*ngFor被稱為“結(jié)構(gòu)型指令”。更多內(nèi)容見 結(jié)構(gòu)型指令和模板語法。
屬性綁定語法
[class.selected]="hero === selectedHero"
更多信息見模板語法。
多個(gè)組件
把主從結(jié)構(gòu)的頁(yè)面重構(gòu)成多個(gè)組件。每個(gè)子組件只聚焦在一個(gè)特定的任務(wù)或工作流上。 最后,AppComponent 將會(huì)變成一個(gè)簡(jiǎn)單的殼,用來作為那些子組件的宿主。
該部分重點(diǎn)是理解思想和概念。
該部分的在線例子。
遵循單一職責(zé)原則(Wiki / 百度百科),創(chuàng)建復(fù)用組件。
文件名和組件名遵循風(fēng)格指南中的標(biāo)準(zhǔn)方式。
- 組件的類名應(yīng)該是大駝峰形式,并且以
Component結(jié)尾:HeroDetailComponent - 組件的文件名應(yīng)該是小寫中線形式,并且以
.component.ts結(jié)尾:hero-detail.component.ts
命名約定:詞匯表-中線命名法。
也稱為烤串命名法(kebab-case)。通常用于指令的選擇器和文件名。
Angular風(fēng)格指南建議每個(gè)文件中只有一個(gè)類。
輸入屬性與目標(biāo)屬性,參考屬性型指令相關(guān)內(nèi)容。
@Input()裝飾器用來表明輸入屬性。更多信息參考屬性型指令相關(guān)內(nèi)容。
每個(gè)組件都必須在一個(gè)(且只有一個(gè))Angular 模塊中聲明。
樣例 src/app/app.module.ts
import { HeroDetailComponent } from './hero-detail.component';
更多知識(shí),參見 Angular 模塊頁(yè)。
服務(wù)
該部分的在線例子。
使用單獨(dú)的服務(wù)可以保持組件精簡(jiǎn),使其集中精力為視圖提供支持,并且,借助模擬(Mock)服務(wù),可以更容易的對(duì)組件進(jìn)行單元測(cè)試。
@Injectable() 裝飾器。注意不要忘記括號(hào),否則會(huì)導(dǎo)致一個(gè)很難診斷的錯(cuò)誤。
無論是出于提高統(tǒng)一性還是減少變更的目的, 都應(yīng)該從一開始就加上
@Injectable()裝飾器。
術(shù)語:Method stub,方法存根
注入服務(wù)
- 添加一個(gè)構(gòu)造函數(shù),并定義一個(gè)私有屬性。
- 添加組件的
providers元數(shù)據(jù)。
更多信息見依賴注入
注意構(gòu)造函數(shù)的功能,它的作用是簡(jiǎn)單的初始化工作,不要把復(fù)雜的邏輯放在構(gòu)造函數(shù)中。
ngOnInit 生命周期鉤子
Angular提供了一些接口,用來介入組件生命周期的幾個(gè)關(guān)鍵時(shí)間點(diǎn):剛創(chuàng)建時(shí)、每次變化時(shí),以及最終被銷毀時(shí)。
import { OnInit } from '@angular/core';
export class AppComponent implements OnInit {
ngOnInit(): void {
}
}
更多信息見生命周期鉤子
異步服務(wù)與承諾
樣例 src/app/hero.service.ts
getHeroes(): Promise<Hero[]> {
return Promise.resolve(HEROES);
}
樣例 src/app/app.component.ts
getHeroes(): void {
this.heroService.getHeroes().then(heroes => this.heroes = heroes);
}
慢
模擬慢速連接,可以據(jù)此觀察應(yīng)用在網(wǎng)絡(luò)較差情況下的表現(xiàn)。
getHeroesSlowly(): Promise<Hero[]> {
return new Promise(resolve => {
// Simulate server latency with 2 second delay
setTimeout(() => resolve(this.getHeroes()), 2000);
});
}
總結(jié)
教程就暫時(shí)進(jìn)行到這里。
教程里的路由這一章內(nèi)容較多,小鐳決定先通過官方文檔把目前接觸到的知識(shí)詳細(xì)地看一遍,深刻理解后再繼續(xù)。