iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 20
1
Modern Web

從零開始的點餐系統,Google好棒棒系列 第 20

[Day20] Angular 主要概念 - Services 與依賴注入

Service 簡介

Angular Services 基本上依然是個 class,他常常是有明確職責定義的,有可能是與後端要數據、驗證使用者的輸入或是 log 服務。相較於先前提到的 Components,Services 較注重可共用且 UI 沒那麼直接相關的邏輯。所以 Services 通常可提供 method 或可共用的暫存資料給不同的 Component 使用。

依賴注入簡介

對應用程式來說依賴注入 (DI) 是很重要的設計模式。在 Angular 中有他自己的 DI 架構來提高程式的模組化程度。被依賴的那個東西有可能是我們剛剛提到的 service ,且可以藉由調整 provider 的參考替換掉被依賴的那個東西。

建立 service

建立骨架

我們在 merchant 資料夾與 core 資料夾建立名為 service 的資料夾,並使用 Angular CLI 產生 service 的骨架(第 1~3 行)。之後在安裝深拷貝物件的套件,在 cloner service 會使用到。

ng g s services/merchants
cd ../core
ng g s services/cloner
npm i --save clone

cloner service

這個 service 的用途在於對物件進行深拷貝產生 immutable objects,而 immutable objects 對開發上與應用程式效能上有什麼好處這邊就不說明了,有興趣可參考這篇文章

cloner sevice 本身是個 class ,在這提供 deepClone 的方法,可提供給注入這個 service 的 class 使用。在程式碼的第 4~5 行,是 Angular 的裝飾器,用來定義 service, providedIn: 'root' 是宣告這個 service 應該在 root application injector 的層級被建立,詳細可參考官方文件

import { Injectable } from '@angular/core';
import * as clone from 'clone';

@Injectable({
  providedIn: 'root',
})
export class ClonerService {
  constructor() {}

  deepClone<T>(value: T): T {
    return clone<T>(value);
  }
}

merchants service

merchants service 目標是處理商家的狀態,所以會有它會提供以下方法
getMerchants、getMerchantById、createMerchant、updateMerchant、deleteMerchant,實作邏輯的部分我自己用 immutable objects 更新 merchants$ 這個 BehaviorSubject,利用數據流的方式暫存商家狀態,並在更新時推送給有訂閱的地方。

在第 12 行,注入剛剛建立的 cloner service,就可以在這個 class 內使用了。

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ClonerService } from 'src/app/core/services/cloner.service';
import { Merchant } from '../models/merchant.model';

@Injectable({
  providedIn: 'root',
})
export class MerchantsService {
  merchants$ = new BehaviorSubject(fakeMerchants);
  constructor(private clonerService: ClonerService) {}

  getMerchants(): Observable<Merchant[]> {
    return this.merchants$.asObservable();
  }

  getMerchantById(id): Observable<Merchant> {
    return this.merchants$.pipe(
      map((val) => val.find((merchant) => merchant.id === id))
    );
  }

  createMerchant(merchant): void {
    let updatedMerchants = this.clonerService.deepClone(
      this.merchants$.getValue()
    );
    updatedMerchants = [
      ...updatedMerchants,
      { ...merchant, id: (+new Date() + Math.random()).toString() },
    ];
    this.merchants$.next(updatedMerchants);
  }

  updateMerchant(merchant): void {
    const updatedMerchants = this.clonerService.deepClone(
      this.merchants$.getValue()
    );
    const merchantIndex = updatedMerchants.findIndex(
      (merchantData) => merchantData.id === merchant.id
    );
    updatedMerchants[merchantIndex] = merchant;
    this.merchants$.next(updatedMerchants);
  }

  deleteMerchant(merchantId): void {
    let updatedMerchants = this.clonerService.deepClone(
      this.merchants$.getValue()
    );
    updatedMerchants = updatedMerchants.filter(
      (merchantData) => merchantData.id !== merchantId
    );
    this.merchants$.next(updatedMerchants);
  }
}

更新 merchant-list.component.ts

這時將 merchants service 注入到 merchant list component (第 8 行),並將 createMerchant、updateMerchant、deleteMerchant 交給依賴去實作,component 只要把參數傳出去就好了。

component 的屬性改成 merchants$,是一個 Observable ,且在 component 的 template (用 async pipe)有做訂閱,所以當 merchants 有變化時,畫面就會依數據改變。(詳細程式看今日程式碼)

// ...省略
export class MerchantListComponent implements OnInit {
  merchants$: Observable<Merchant[]>;

  constructor(private merchantsService: MerchantsService) {}

  ngOnInit(): void {
    this.merchants$ = this.merchantsService.getMerchants();
  }

  openEditModal(mode, merchantId?): void {
    console.log('mode', mode);
    console.log('id', merchantId);
  }

  createMerchant(merchant): void {
    this.merchantsService.createMerchant(merchant);
  }

  updateMerchant(merchant): void {
    this.merchantsService.updateMerchant(merchant);
  }

  deleteMerchant(merchantId): void {
    this.merchantsService.deleteMerchant(merchantId);
  }
}

結語

這邊只是很簡單的介紹 service 與依賴注入,事實上 Angular 的依賴注入非常強大、彈性很高,有興趣了解的人一定要讀官方文件的介紹,這邊因為專案還沒複雜到那種程度所以就沒詳細解說了,今天的程式碼在這裡。下一篇要講 Angular 的表單,用使用者的資料創建新的商家資訊或更新商家資訊。


上一篇
[Day19] Angular 主要概念 - Component 實作(2)
下一篇
[Day21] Angular 主要概念 - 表單功能
系列文
從零開始的點餐系統,Google好棒棒30

尚未有邦友留言

立即登入留言