angular quick start tutorial summary
Angular Home
angular.cn
angular.io
angular-in-memory-web-api
Creat
你使用 CLI 建立了第二個元件 HeroesComponent。
ng generate component heroes
你把 HeroesComponent 新增到了殼元件 AppComponent 中,以便顯示它。
app.component.html <app-heros></app-heros>
你使用 UppercasePipe 來格式化英雄的名字。
heros.component.html <h2>{{hero.name | uppercase}} Details</h2>
你用 ngModel 指令實現了雙向資料繫結。
<input [(ngModel)]="hero.name" placeholder="name">
你知道了 AppModule。
新增元資料的地方,Angular 需要知道如何把應用程式的各個部分組合到一起,以及該應用需要哪些其它檔案和庫。 這些資訊被稱為元資料(metadata)
你把 FormsModule 匯入了 AppModule,以便 Angular 能識別並應用 ngModel 指令。
app.module.ts import { FormsModule } from '@angular/forms'; // <-- NgModel lives here imports: [ BrowserModule, FormsModule ],
你知道了把元件宣告到 AppModule 是很重要的,並認識到 CLI 會自動幫你宣告它。
@NgModule({ declarations: [AppComponent,HerosComponent],。。。})
Display
英雄指南應用在一個主從檢視中顯示了英雄列表。
click事件 html <li *ngFor="let hero of heroes" (click)="onSelect(hero)"> ts onSelect(hero: Hero): void {this.selectedHero = hero;}
使用者可以選擇一個英雄,並檢視該英雄的詳情。
<input [(ngModel)]="selectedHero.name" placeholder="name">
你使用 *ngFor 顯示了一個列表。
<li *ngFor="let hero of heroes" [class.selected]='hero === selectHero' (click)="onSelect(hero)">
你使用 *ngIf 來根據條件包含或排除了一段 HTML。
<div *ngIf='selectHero'></div>
你可以用 class 繫結來切換 CSS 的樣式類。
[class.selected]='hero === selectHero'
Master/Detail
你建立了一個獨立的、可複用的 HeroDetailComponent 元件。
ng generate component hero-detail
你用屬性繫結語法來讓父元件 HeroesComponent 可以控制子元件 HeroDetailComponent。 import { Hero } from '../hero'; @Input() hero: Hero;
你用 @Input 裝飾器來讓 hero 屬性可以在外部的 HeroesComponent 中繫結
<app-hero-detail [hero]="selectedHero"></app-hero-detail> import { Input } from '@angular/core';
Service
你把資料訪問邏輯重構到了 HeroService 類中。
元件不應該直接獲取或儲存資料, 它們應該聚焦於展示資料,而把資料訪問的職責委託給某個服務
你在根模組 AppModule 中提供了 HeroService 服務,以便在別處可以注入它。
ng generate service hero --module=app //not insert automatically import { HeroService } from './hero.service'; @NgModule({providers: [HeroService]})
你使用 Angular 依賴注入機制把它注入到了元件中。
heroes.component.ts: import { HeroService } from '../hero.service'; constructor(private heroService: HeroService) { }
你給 HeroService 中獲取資料的方法提供了一個非同步的函式簽名。
服務是在多個“互相不知道”的類之間共享資訊的好辦法
回撥函式,可以返回 Promise(承諾),也可以返回 Observable(可觀察物件)
你發現了 Observable 以及 RxJS 庫。
import { Observable, of } from 'rxjs';
你使用 RxJS 的 of() 方法返回了一個模擬英雄資料的可觀察物件 (Observable<Hero[]>)。 getHeroes(): Observable<Hero[]> { this.messageService.add('HeroService: fetched heroes'); return of(HEROES); }
在元件的 ngOnInit 生命週期鉤子中呼叫 HeroService 方法,而不是建構函式中。
讓建構函式保持簡單,只做初始化操作,比如把建構函式的引數賦值給屬性。 建構函式不應該做任何事
你建立了一個 MessageService,以便在類之間實現鬆耦合通訊。
ng generate service message --module=app
HeroService 連同注入到它的服務 MessageService 一起,注入到了元件中。
hero.service.ts: import { MessageService } from './message.service'; constructor(private messageService: MessageService) { } export class HeroesComponent implements OnInit { constructor(private heroService: HeroService) {} ngOnInit() {this.getHeroes();} getHeroes(): void {this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);} }
Router
添加了 Angular 路由器在各個不同元件之間導航。
ng generate module app-routing --flat --module=app
你使用一些 <a>
連結和一個<router-outlet>
把 AppComponent 轉換成了一個導航用的殼元件。
`app.component.html
HTTP
你添加了在應用程式中使用 HTTP 的必備依賴。
hero.service.ts import { HttpClient, HttpHeaders } from '@angular/common/http'; constructor( private http: HttpClient, private messageService: MessageService) { }
你重構了 HeroService,以通過 web API 來載入英雄資料。
return this.http.get<Hero[]>(this.heroesUrl) //return of(HEROES);
.pipe(
tap(heroes => this.log(`fetch heroes`)),
catchError(this.handleError('getHeroes', [])
));
}```
你擴充套件了 HeroService 來支援 post()、put() 和 delete() 方法。
```hero.service.ts
updateHero(hero: Hero): Observable<any> {
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
tap(() => this.log(`update hero id = ${hero.id}`)),
catchError(this.handleError<any>('updateHero'))
);
}
addHero(hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
tap(() => this.log(`add hero w/ id=${hero.id}`)),
catchError(this.handleError<Hero>('addHero'))
);
}
deleteHero(hero: Hero | number): Observable<Hero> {
const id = typeof hero === 'number' ? hero : hero.id;
const url = `${this.heroesUrl}/${id}`;
return this.http.delete<Hero>(url, httpOptions).pipe(
tap(() => this.log(`delete hero id=${id}`)),
catchError(this.handleError<Hero>('deleteHero'))
);
}```
你修改了元件,以允許使用者新增、編輯和刪除英雄。
`heroes.component.html
<label>Hero Name:
<input #heroName>
</label>
<button (click)="add(heroName.value);heroName.value=''">add</button>
heroes.component.ts
add(name:string):void{
name=name.trim();
if(!name) return;
this.heroService.addHero({name} as Hero)
.subscribe(hero=>{this.heroes.push(hero);});
}`
你配置了一個記憶體 Web API。
`npm install angular-in-memory-web-api --save
app.module.ts
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';
in-memory-data.service.ts
import { InMemoryDbService } from 'angular-in-memory-web-api';
export class InMemoryDataService implements InMemoryDbService {
construct (){}
createDb() {
const heroes = [
{ id: 11, name: 'Mr. Nice' } ];
return {heroes};
}
}`
你學會了如何使用“可觀察物件”。
`通常,Observable 可以在一段時間內返回多個值。
但來自 HttpClient 的 Observable 總是發出一個值,然後結束,再也不會發出其它值。
使用 RxJS 的 catchError() 操作符來建立對 Observable 結果的處理管道(pipe)
import { catchError, map, tap } from 'rxjs/operators';
現在,使用 .pipe() 方法來擴充套件 Observable 的結果,並給它一個 catchError() 操作符。
getHeroes (): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
catchError(this.handleError('getHeroes', []))
);
}`
使用 RxJS的tap檢視Observable中的值,使用那些值做一些事情,並且把它們傳出來.這種tap回撥不會改變這些值本身
```/** GET heroes from the server */
getHeroes (): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
tap(heroes => this.log(`fetched heroes`)),
catchError(this.handleError('getHeroes', []))
);
}
基本概念
架構概覽
Angular 是一個用 HTML 和 TypeScript 構建客戶端應用的平臺與框架。 Angular 本身使用 TypeScript 寫成的。它將核心功能和可選功能作為一組 TypeScript 庫進行實現,你可以把它們匯入你的應用中。
Angular 的基本構造塊是 NgModule,它為元件提供了編譯的上下文環境。 NgModule 會把相關的程式碼收集到一些功能集中。Angular 應用就是由一組 NgModule 定義出的。 應用至少會有一個用於引導應用的根模組,通常還會有很多特性模組。
元件定義檢視。檢視是一組可見的螢幕元素,Angular 可以根據你的程式邏輯和資料來選擇和修改它們。 每個應用都至少有一個根元件。
元件使用服務。服務會提供那些與檢視不直接相關的功能。服務提供商可以作為依賴被注入到元件中, 這能讓你的程式碼更加模組化、可複用,而且高效。
元件和服務都是簡單的類,這些類使用裝飾器來標出它們的型別,並提供元資料以告知 Angular 該如何使用它們。
元件類的元資料將元件類和一個用來定義檢視的模板關聯起來。 模板把普通的 HTML 和指令與繫結標記(markup)組合起來,這樣 Angular 就可以在呈現 HTML 之前先修改這些 HTML。
服務的元資料提供了一些資訊,Angular 要用這些資訊來讓元件可以通過依賴注入(DI)使用該服務。
應用的元件通常會定義很多檢視,並進行分級組織。 Angular 提供了 Router 服務來幫助你定義檢視之間的導航路徑。 路由器提供了先進的瀏覽器內導航功能。
模組
Angular 定義了 NgModule,它和 JavaScript(ES2015) 的模組不同而且有一定的互補性。 NgModule 為一個元件集聲明瞭編譯的上下文環境,它專注於某個應用領域、某個工作流或一組緊密相關的能力。 NgModule 可以將其元件和一組相關程式碼(如服務)關聯起來,形成功能單元。
每個 Angular 應用都有一個根模組,通常命名為 AppModule。根模組提供了用來啟動應用的引導機制。 一個應用通常會包含很多功能模組。
像 JavaScript 模組一樣,NgModule 也可以從其它 NgModule 中匯入功能,並允許匯出它們自己的功能供其它 NgModule 使用。 比如,要在你的應用中使用路由器(Router)服務,就要匯入 Router 這個 NgModule。
把你的程式碼組織成一些清晰的功能模組,可以幫助管理複雜應用的開發工作並實現可複用性設計。 另外,這項技術還能讓你獲得惰性載入(也就是按需載入模組)的優點,以儘可能減小啟動時需要載入的程式碼體積。
更深入的討論,參見模組簡介。
元件
每個 Angular 應用都至少有一個元件,也就是根元件,它會把元件樹和頁面中的 DOM 連線起來。 每個元件都會定義一個類,其中包含應用的資料和邏輯,並與一個 HTML 模板相關聯,該模板定義了一個供目標環境下顯示的檢視。
@Component 裝飾器表明緊隨它的那個類是一個元件,並提供模板和該元件專屬的元資料。
裝飾器是一些用於修飾 JavaScript 類的函式。Angular 定義了許多裝飾器,這些裝飾器會把一些特定種類的元資料附加到類上,以便 Angular 瞭解這些這些類的含義以及該如何使用它們。
到網上學習關於裝飾器的更多知識。
模板、指令和資料繫結
模板會把 HTML 和 Angular 的標記(markup)組合起來,這些標記可以在 HTML 元素顯示出來之前修改它們。 模板中的指令會提供程式邏輯,而繫結標記會把你應用中的資料和 DOM 連線在一起。
事件繫結讓你的應用可以通過更新應用的資料來響應目標環境下的使用者輸入。
屬性繫結讓你將從應用資料中計算出來的值插入到 HTML 中。
在檢視顯示出來之前,Angular 會先根據你的應用資料和邏輯來執行模板中的指令並解析繫結表示式,以修改 HTML 元素和 DOM。 Angular 支援雙向資料繫結,這意味著 DOM 中發生的變化(比如使用者的選擇)同樣可以反映回你的程式資料中。
你的模板也可以用管道轉換要顯示的值以增強使用者體驗。比如,可以使用管道來顯示適合使用者所在地區的日期和貨幣格式。 Angular 為一些通用的轉換提供了預定義管道,你還可以定義自己的管道。
要了解對這些概念的深入討論,參見元件介紹。
服務與依賴注入
對於與特定檢視無關並希望跨元件共享的資料或邏輯,可以建立服務類。 服務類的定義通常緊跟在 “@Injectable” 裝飾器之後。該裝飾器提供的元資料可以讓你的服務作為依賴被注入到客戶元件中。
依賴注入(或 DI)讓你可以保持元件類的精簡和高效。有了 DI,元件就不用從伺服器獲取資料、驗證使用者輸入或直接把日誌寫到控制檯,而是會把這些任務委託給服務。
更深入的討論,參見服務和 DI 簡介。
路由
Angular 的 Router 模組提供了一個服務,它可以讓你定義在應用的各個不同狀態和檢視層次結構之間導航時要使用的路徑。 它的工作模型基於人們熟知的瀏覽器導航約定:
在位址列輸入 URL,瀏覽器就會導航到相應的頁面。
在頁面中點選連結,瀏覽器就會導航到一個新頁面。
點選瀏覽器的前進和後退按鈕,瀏覽器就會在你的瀏覽歷史中向前或向後導航。
不過路由器會把類似 URL 的路徑對映到檢視而不是頁面。 當用戶執行一個動作時(比如點選連結),本應該在瀏覽器中載入一個新頁面,但是路由器攔截了瀏覽器的這個行為,並顯示或隱藏一個檢視層次結構。
如果路由器認為當前的應用狀態需要某些特定的功能,而定義此功能的模組尚未載入,路由器就會按需惰性載入此模組。
路由器會根據你應用中的導航規則和資料狀態來攔截 URL。 當用戶點選按鈕、選擇下拉框或收到其它任何來源的輸入時,你可以導航到一個新檢視。 路由器會在瀏覽器的歷史日誌中記錄這個動作,所以前進和後退按鈕也能正常工作。
要定義導航規則,你就要把導航路徑和你的元件關聯起來。 路徑(path)使用類似 URL 的語法來和程式資料整合在一起,就像模板語法會把你的檢視和程式資料整合起來一樣。 然後你就可以用程式邏輯來決定要顯示或隱藏哪些檢視,以根據你制定的訪問規則對使用者的輸入做出響應。
更深入的討論,參見路由與導航。
接下來呢?
What’s next
你已經學完了 Angular 應用的主要構造塊的基礎知識。 下面這張圖展示了這些基礎部分之間是如何關聯起來的。
overview
元件和模板共同定義了 Angular 的檢視。
元件類上的裝飾器為其添加了元資料,其中包括指向相關模板的指標。
元件模板中的指令和繫結標記會根據程式資料和程式邏輯修改這些檢視。
依賴注入器會為元件提供一些服務,比如路由器服務就能讓你定義如何在檢視之間導航。