Service & DI

Service

Service 允許你在應用程式中共享邏輯和資料。 放共用的邏輯跟資料,讓這些資料可以在不同的 component 中被使用。

基本的 Service 結構

import { Injectable } from '@angular/core';

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

依賴注入階層

依賴解析順序: Component → ElementInjector → Application root EnvironmentInjector / ModuleInjector → Platform EnvironmentInjector → NullInjector (錯誤)

提供 Service 的方法

1. 透過 @Injectable 裝飾器(推薦)

@Injectable({
  providedIn: 'root',
})

這種方式會讓 service 在整個應用程式中以單一實例存在,並且使用 Tree-shakable 的方式只在需要時才會被包含進最終的 bundle 中。

2. 在 main.ts 中提供

// 原本
bootstrapApplication(AppComponent).catch((err) => console.error(err));

// 改成
bootstrapApplication(AppComponent, { providers: [TasksService] }).catch((err) =>
  console.error(err)
);

這個方法會讓 Angular 在最一開始初始化時就載入這個 service。如果所有服務都用這個方法註冊,可能會導致載入速度變慢。

3. 在 Component 中提供

@Component({
  selector: 'app-tasks',
  standalone: true,
  templateUrl: './tasks.component.html',
  imports: [NewTaskComponent, TasksListComponent],
  providers: [TasksService],
})
export class TasksComponent {}

這個方法讓 service 只在這個 component 以及這個 component 的子元件中可用。 注意:這樣會創建不同的 service 實例,不同於 root 提供的單一實例。

依賴注入 (DI)

component、directive、service 可以請求他們所依賴的值(dependency),然後由 Angular 提供。 不需要自己建立 service 實例,而是從 Angular 請求。

bootstrapApplication(AppComponent, { providers: [TasksService] }).catch((err) =>
  console.error(err)
);

實際上,Angular 會自動幫你將 TasksService 作為 Token:

export const TasksServiceToken = new InjectionToken<TasksService>('tasks-service-token');

bootstrapApplication(AppComponent, { 
  providers: [{ provide: TasksServiceToken, useClass: TasksService }] 
}).catch((err) => console.error(err));

這樣可以改變 token 的名稱,但在其他地方要注入 service 時就要使用你新創建的 token 名稱:

constructor(@Inject(TasksServiceToken) private tasksService: TasksService) {}

自定義 Providers

也可以藉此自定義 providers,例如提供常數或配置:

export const TASK_STATUS_OPTIONS = new InjectionToken<TaskStatusOptions>(
  'task-status-options'
);

export const TaskStatusOptions: TaskStatusOptions = [
  { value: 'open', taskStatus: 'OPEN', text: 'Open' },
  { value: 'in-progress', taskStatus: 'IN_PROGRESS', text: 'In-progress' },
  { value: 'done', taskStatus: 'DONE', text: 'Done' },
];

export const taskStatusOptionsProvider: Provider = {
  provide: TASK_STATUS_OPTIONS,
  useValue: TaskStatusOptions,
};

在 component 中使用 inject 函數注入:

// 在 component 類別內部
taskStatusOptions = inject(TASK_STATUS_OPTIONS);

Last updated