大多數前端應用都要透過 HTTP 協議與伺服器通訊,才能下載或上傳資料並訪問其它後端服務。Angular 給應用提供了一個簡化的 HTTP 客戶端 API,也就是 @angular/common/http 中的 HttpClient 服務類別。
在使用 HTTPClientModule 之前,你應該對下列內容有基本的瞭解:
使用 HttpClient,就要先匯入 Angular 的 HttpClientModule,HttpClient 服務為所有工作都使用了可觀察物件返回一個 Observable,使用 ES7 的 Async/Await 機制,以便在業務邏輯層面直接調用,就算為了完成一個功能而需要調用許多 API,也不怕陷入回調地獄。
- HttpClient 返回一個 Observable,可配合 pipe 享受 Rxjs/operator 所帶來的便利。
- Async/Await 使用避免回調地獄。
包裝過後的 Http 服務大致規格如下

例如 Http GET 服務

使用方式如下
async httpService() {
let botType = await this.httpService.httpGET('/robot/botType'); // GET取得 botType
let botData = await this.httpService.httpGET('/robot/botData?botType=' + botType); // GET取得 botData
this.httpService.httpPOST(botData); // POST寫入 botData
}
一個 Angular 專案通常會有一些公用元件或服務的模組,目的是為了方便管理維護與重複使用,容易匯入到其他中專案或模組中。
舉個例子 Angular 都會有的模組,像是 Shared Module,就可以讓你在 Angular 專案的功能模組中,一次性引入公用元件不必到功能模組中個別引入。
本篇要做的是先建立一個公用的 Http 服務,對 Angular 的 HttpClient 服務進行再包裝 (使用 async/await)。
STEP 1. 新增一個資料夾 services/
> cd /src/app/services
> ng g s http
CREATE src/app/services/http.service.spec.ts (347 bytes)
CREATE src/app/services/http.service.ts (133 bytes)
STEP 2. src\app\services\http.service.ts 建立服務方法,
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { lastValueFrom } from 'rxjs';
import { tap } from 'rxjs/operators';
/**
* interface of API Response
*
* @export
* @interface IHttpResponse
*/
export interface IHttpResponse {
data?: object | [];
}
/**
* Http Service
*
* @export
* @class HttpService
*/
@Injectable({
providedIn: 'root',
})
export class HttpService {
/**
* @ignore
*/
constructor(private httpClient: HttpClient) {}
/**
* ### httpGET
* > HttpClient Member - Http GET
* >
* > Observable to Promise(for Async/Await)
*
* @param {*} args[] - (args[0] : url)
* @returns {Promise<object>}
* @memberof HttpService
*/
async httpGET(...args: any[]): Promise<object> {
const apiUrl = args[0] || '';
try {
const result$ = this.httpClient.get(apiUrl).pipe(
tap((resp: IHttpResponse) => {
return resp;
})
);
return await lastValueFrom(result$);
} catch (err) {
return Promise.reject(err);
}
}
}
由於 Rxjs toPromise() deprecated 因此
toPromise()改成lastValueFrom()。
STEP 3. 根模組 src\app\app.module.ts 引入 HttpClientModule
由於 Http providedIn 到 root 所以我們在根模組引入 HttpClientModule。
// Angular HttpClientModule
import { HttpClientModule } from '@angular/common/http';
// Home
import { HomeComponent } from './home/home.component';
@NgModule({
declarations: [AppComponent, HomeComponent],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
HttpClientModule,
NbThemeModule.forRoot({ name: 'default' }),
NbEvaIconsModule,
...NEBULAR_ROOT,
...NEBULAR_ALL,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
STEP 4. 在 HomeComponent 注入 HttpService 服務到 src\app\home\home.component.ts
import { Component, OnInit } from '@angular/core';
import { NbSidebarService } from '@nebular/theme';
// 使用公用服務 HttpService
import { HttpService } from '/services/http.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
})
export class HomeComponent implements OnInit {
constructor(private sidebarService: NbSidebarService, private httpService: HttpService) {}
toggle() {
this.sidebarService.toggle(true);
return false;
}
async ngOnInit(): Promise<void> {
const resp = await this.httpService.httpGET('http://localhost:3000/myapp-hello-mockdata');
console.log(resp);
}
}
範例使用上一篇做的 Mock API
myapp-hello-mockdata
src\app\home\home.component.ts
async ngOnInit(): Promise<void> {
const resp = await this.httpService.httpGET('http://localhost:3000/myapp-hello-mockdata');
console.log(resp);
}
範例的執行結果如下

查看 Compodoc 註解網站得知目前專案結構如下,

服務的 @Injectable providedIn 選項確定哪些注入器將提供可注入。
@Injectable({
providedIn: 'root',
})
providedIn 可接受的參數有,
providedIn?: Type<any> | 'root' | 'platform' | 'any' | null
'root':在大多數應用程式中是指應用程式級注入器。'platform':由頁面上所有應用程式共享的特殊單例平臺注入器。'any':在每個延遲載入的模組中提供一個唯一實例,而所有熱切載入的模組共享一個實例。null:等效於 undefined 。可注入物不會在任何範圍內自動提供,必須新增到 @NgModule 、 @Component 或 @Directive的 providers 陣列中。使用 @Injectable() 的 providedIn 屬性優於 @NgModule() 的 providers 陣列,因為使用 @Injectable() 的 providedIn 時,優化工具可以進行搖樹優化,從而刪除你的應用程式中未使用的服務,以減小捆綁套件尺寸。
新建服務都會自動 providedIn 到
root,這樣可以不用像以前去維護providers 陣列。
本篇學習使用 Angular 建一個 Http 服務,包裝一個適用於 (async/await) 的 Http 服務,providedIn 到根模組 AppModule 後再把服務注入到 HomeComponent 的流程。
接下來讓我們完善 Http 服務的 CRUD API 再來實作租戶管理功能吧。