大多數前端應用都要透過 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 再來實作租戶管理功能吧。