SKILL.md
唯讀
名稱
angular-di
描述
在 Angular v20+ 中使用 inject()、注入令牌和提供者配置來實作依賴注入。適用於服務架構、在不同層級提供依賴、建立可注入令牌,以及管理單例與範圍服務。觸發時機:建立服務、設定提供者、使用注入令牌,或理解 DI 階層。
Angular 依賴注入
使用 inject() 和提供者在 Angular v20+ 中配置和使用依賴注入。
基本注入
使用 inject()
建議優先使用 inject() 而非建構子注入:
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { User } from './user.service';
@Component({
selector: 'app-user-list',
template: `...`,
})
export class UserList {
// 注入依賴
private http = inject(HttpClient);
private userService = inject(User);
// 可立即使用
users = this.userService.getUsers();
}
可注入服務
import { Injectable, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root', // 根層級單例
})
export class User {
private http = inject(HttpClient);
private users = signal<User[]>([]);
readonly users$ = this.users.asReadonly();
async loadUsers() {
const users = await firstValueFrom(
this.http.get<User[]>('/api/users')
);
this.users.set(users);
}
}
提供者範圍
根層級(單例)
// 建議使用 providedIn
@Injectable({
providedIn: 'root',
})
export class Auth {}
// 或在 app.config.ts 中
export const appConfig: ApplicationConfig = {
providers: [
Auth,
],
};
元件層級(每個元件一個實例)
@Component({
selector: 'app-editor',
providers: [EditorState], // 每個元件新實例
template: `...`,
})
export class Editor {
private editorState = inject(EditorState);
}
路由層級
export const routes: Routes = [
{
path: 'admin',
providers: [Admin], // 在此路由樹內共享
children: [
{ path: '', component: AdminDashboard },
{ path: 'users', component: AdminUsers },
],
},
];
注入令牌
建立令牌
import { InjectionToken } from '@angular/core';
// 簡單值令牌
export const API_URL = new InjectionToken<string>('API_URL');
// 物件令牌
export interface AppConfig {
apiUrl: string;
features: {
darkMode: boolean;
analytics: boolean;
};
}
export const APP_CONFIG = new InjectionToken<AppConfig>('APP_CONFIG');
// 帶工廠的令牌(自我提供)
export const WINDOW = new InjectionToken<Window>('Window', {
providedIn: 'root',
factory: () => window,
});
export const LOCAL_STORAGE = new InjectionToken<Storage>('LocalStorage', {
providedIn: 'root',
factory: () => localStorage,
});
提供令牌值
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
{ provide: API_URL, useValue: 'https://api.example.com' },
{
provide: APP_CONFIG,
useValue: {
apiUrl: 'https://api.example.com',
features: { darkMode: true, analytics: true },
},
},
],
};
注入令牌
@Injectable({ providedIn: 'root' })
export class Api {
private apiUrl = inject(API_URL);
private config = inject(APP_CONFIG);
private window = inject(WINDOW);
getBaseUrl(): string {
return this.apiUrl;
}
}
提供者類型
useClass
// 提供實作
{ provide: Logger, useClass: ConsoleLogger }
// 條件式實作
{
provide: Logger,
useClass: environment.production
? ProductionLogger
: ConsoleLogger,
}
useValue
// 靜態值
{ provide: API_URL, useValue: 'https://api.example.com' }
// 設定物件
{ provide: APP_CONFIG, useValue: { theme: 'dark', language: 'en' } }
useFactory
// 帶依賴的工廠
{
provide: User,
useFactory: (http: HttpClient, config: AppConfig) => {
return new User(http, config.apiUrl);
},
deps: [HttpClient, APP_CONFIG],
}
// 非同步工廠(不建議,請使用 provideAppInitializer)
{
provide: CONFIG,
useFactory: () => fetch('/config.json').then(r => r.json()),
}
useExisting
// 別名指向現有提供者
{ provide: AbstractLogger, useExisting: ConsoleLogger }
// 多個令牌指向同一實例
providers: [
ConsoleLogger,
{ provide: Logger, useExisting: ConsoleLogger },
{ provide: ErrorLogger, useExisting: ConsoleLogger },
]
注入選項
可選注入
@Component({...})
export class My {
// 若未提供則回傳 null
private analytics = inject(Analytics, { optional: true });
trackEvent(name: string) {
this.analytics?.track(name);
}
}
Self、SkipSelf、Host
@Component({
providers: [Local],
})
export class Parent {
// 僅在此元件的注入器中查找
private local = inject(Local, { self: true });
}
@Component({...})
export class Child {
// 跳過此元件,在父層查找
private parentService = inject(ParentSvc, { skipSelf: true });
// 僅向上查找到宿主元件
private hostService = inject(Host, { host: true });
}
多重提供者
為同一令牌收集多個值:
// 多個驗證器的令牌
export const VALIDATORS = new InjectionToken<Validator[]>('Validators');
// 提供多個值
providers: [
{ provide: VALIDATORS, useClass: RequiredValidator, multi: true },
{ provide: VALIDATORS, useClass: EmailValidator, multi: true },
{ provide: VALIDATORS, useClass: MinLengthValidator, multi: true },
]
// 以陣列形式注入
@Injectable()
export class Validation {
private validators = inject(VALIDATORS); // Validator[]
validate(value: string): ValidationError[] {
return this.validators
.map(v => v.validate(value))
.filter(Boolean);
}
}
HTTP 攔截器(多重提供者)
// 攔截器內部使用多重提供者
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
authInterceptor,
loggingInterceptor,
errorInterceptor,
])
),
],
};
應用程式初始化器
使用 provideAppInitializer 在應用程式啟動前執行非同步程式碼:
import { provideAppInitializer, inject } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
Config,
provideAppInitializer(() => {
const configService = inject(Config);
return configService.loadConfig();
}),
],
};
多個初始化器
providers: [
provideAppInitializer(() => {
const config = inject(Config);
return config.load();
}),
provideAppInitializer(() => {
const auth = inject(Auth);
return auth.checkSession();
}),
]
環境注入器
以程式方式建立注入器:
import { createEnvironmentInjector, EnvironmentInjector, inject } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class Plugin {
private parentInjector = inject(EnvironmentInjector);
loadPlugin(providers: Provider[]): EnvironmentInjector {
return createEnvironmentInjector(providers, this.parentInjector);
}
}
runInInjectionContext
在注入上下文中執行程式碼:
import { runInInjectionContext, EnvironmentInjector, inject } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class Utility {
private injector = inject(EnvironmentInjector);
executeWithDI<T>(fn: () => T): T {
return runInInjectionContext(this.injector, fn);
}
}
// 使用方式
utilityService.executeWithDI(() => {
const http = inject(HttpClient);
// 使用 http...
});
如需進階模式,請參閱 references/di-patterns.md。






