在昨天我們建立了 Angular 專案、使用 JSON-server 來製作 mock db,並且建立了英雄資料。今天這篇文章我們將完成下列事項:
(現在,我們要試著召喚英靈...)
我知道!再怎麼說,我也是個讀書人呢。
(哦?你知道什麼?)
要召喚英靈對吧,那就要唸這個咒文——「速速前呂布奉先!」。
(呃...)
我知道問題出在哪了。速速前只能召喚物體,所以,我要召喚方天畫戟,這樣拿著方天畫戟的呂布就會一起傳過來——「速速前方天畫戟!」。
(神啊,我是不是做錯了什麼。)
糟糕,難道是因為我沒有夢見媽祖嗎......
(你最好什麼都不要唸,好好看接下來的文章。)
現在,我們在 mock db 裏面存有英雄資料,透過 JSON-server 的協助,可以在 Angular 中來模擬使用 http 來呼叫 api 的行為。在 Angular 中,我們可以引入 HttpClientModule,並使用這個模組所擁有的提供商(provider)HttpClient, 來執行 http 請求。
在引入 HttpClientModule 模組之前,要先簡單說明「模組」(module)概念。「模組」概念解決的問題是,將應用程式更有組織地進行管理,而透過 import、export,就可以很好地達到複用程式碼的目的,在 JavaScript ES6 版本中,正式提出了模組系統 ,也就是「JS 模組」。
而在 Angular 中,模組化包含了兩個部分 :
@NgModule
裝飾器標記的類別,在裝飾器裏面會放置元資料的物件,你也需要在這個物件中說明「宣告」(declarations)、「匯入」(imports)、「匯出」(exports)...分別有哪些類別。我們可以先用「傭兵團」概念來思考 @NgModule
裝飾器記載的元資料:
「傭兵團」概念或許可以說明在 NgModule 使用其他 NgModule 提供的支援服務,但這個概念也存在一個風險:那就是「支援協定並不是雙向的,而是單方面尋求支援」。意思是,你不能在 A 模組匯入 B 模組的情形下,同時在 B 模組匯入 A 模組,這會造成循環匯入的錯誤
。對這個風險有印象,在日後遇到時,你就會知道發生了什麼事情,應該從何著手處理。
現在讓我們看看專案中必定存在的根模組 AppModule(app.module.ts):
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
上方的 import {xxx} from 'path';
即為匯入 JS 模組的語法。而 @NgModule 裡面的各種陣列,即為此模組的相關內容。先看「宣告」(declarations) 陣列,這裏是放置宣告隸屬這個 module 檔案中的類別,現在這裡擁有 AppComponent 元件,如下:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'angular-tour-of-heroes';
}
可以看到,AppComponent 是一個帶有 @Component
(元件)裝飾器的類別。你可以這麼想,帶有 @Component
(元件) 、 @Directive
(指令) 、@Pipe
(管道) 裝飾器的類別,就是通過認證的傭兵。不同的裝飾器,用來說明傭兵負責的不同功用。
並且,在這個世界中不存在個體戶傭兵,所有想要從事傭兵工作的人,都必須加入傭兵團,也只能加入一個傭兵團。具體來說,AppComponent 只能登記在一個 NgModule 的「宣告」(declarations)中,無法再宣吿在其他 NgModule 下;如果沒有宣告在任何 NgModule 中,就沒有人可以使用 AppComponent。
此外,關於帶有元件/指令/管道不同裝飾器的類別,我們慢慢都會使用到。
現在,在根模組 AppModule 「匯入」(imports)陣列中加入 HttpClientModule【與其他傭兵團簽訂支援協議
】,讓我們可以使用 HttpClientModule 匯出(exports)的內容【使用其他傭兵團有提供支援服務的傭兵
】。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Angular 就是一個 JavsScript 框架,連 NgModule
裝飾器,都是作為 JS 模組匯入到需要使用的地方的。
更詳盡的說明,請參考官方文件。
為了更好的演示,讓我們用 Angular CLI 來建立一個 hero-list
元件。進入 src/app 目錄,這裡有個小技巧,在編輯器中,我們可以針對想要建立元件的資料夾點擊滑鼠右鍵,就可以發現「在終端機中打開」(Open in Integrated Terminal),點擊它,編輯器就會為我們打開終端機,並預設在此路徑中。
接著就可以執行下列指令:
ng g c hero-list // g for generate, c for component
Anglaur CLI 就會建立數個檔案,可能包含「.html」、「.ts」、「.css」、「.spec.ts」,並同時更新了 app.module.ts
,讓我們查看更新的檔案:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { HeroListComponent } from './hero-list/hero-list.component';
@NgModule({
declarations: [
AppComponent,
HeroListComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
可以發現,Angualr CLI 幫我們在這個模組中宣告了 HeroListComponent 類別,因此,我們就可以在這個模組中使用它——以這裡來說,我們就可以在 AppComponent 中使用 HeroListComponent。
因為在 app.module.ts
中匯入了 HttpClientModule
模組,我們就可以在宣告在 app.module.ts
的 HeroListComponent 裡,使用 HttpClientModule
匯出的內容,包括 HttpClient。
打開 hero-list.component.ts 檔案,在建構式(constructor)中注入 HttpClient:
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html',
styleUrls: ['./hero-list.component.css']
})
export class HeroListComponent implements OnInit {
constructor(
private http: HttpClient
) { }
ngOnInit(): void {
}
}
然後我們使用它來取得英雄資料,在 ngOnInit 裏撰寫相關程式碼:
constructor(
private http: HttpClient
) { }
ngOnInit(): void {
this.http.get('api/heros').subscribe((heroList) => {
console.log('HeroList', heroList);
});
}
如果現在用瀏覽器打開此 App,console 是不會印出 HeroList 的,因為我們根本還沒在 app 中使用 HeroListComponent 元件。
要怎麼使用元件呢?在 hero-list.component.ts
的 @Component 裝飾器
裡的元資料物件有個屬性 selector: 'app-hedo-list',這個就是我們可以寫在 *.html 檔案中、代表使用此元件的語法,類似 HTML 的 tag。
讓我們在根元件 app.component.html
中使用 HeroListComponent 元件:
<app-hero-list></app-hero-list>
如此我們就可以在 app 中看到如下畫面,代表我們確實取得了英雄資料:
小提醒:當你新增/刪除檔案後,必須重啟專案(ng serve
)才會執行改變後的程式碼。