如何在Angular自定义装饰器中注入并调用单例服务
解决方案
装饰器属于TypeScript编译阶段执行的逻辑,而Angular的依赖注入容器是应用运行时才完成初始化的,因此无法在装饰器定义阶段直接完成服务注入。你的
FeatureManagerService是根级注入的单例,我们可以通过运行时获取根注入器实例的方式调用服务方法。
步骤1:配置应用启动预加载Feature Flag
首先要保证访问装饰器属性前,feature flag已经加载完成,用Angular的APP_INITIALIZER实现启动前预加载:
import { APP_INITIALIZER, NgModule } from '@angular/core'; import { FeatureManagerService } from './feature-manager.service'; // 初始化加载函数 export function preloadFeatureFlags(featureService: FeatureManagerService) { return () => featureService.loadFeatureFlags().toPromise(); } @NgModule({ // ... 其他模块配置 providers: [ { provide: APP_INITIALIZER, useFactory: preloadFeatureFlags, deps: [FeatureManagerService], multi: true } ] }) export class AppModule { }
步骤2:修改装饰器实现,运行时获取服务实例
属性的getter是运行时访问属性才触发,此时DI容器已经初始化完成,我们可以通过两种方式获取服务实例:
方式A:Angular 14+ 版本(推荐)
直接用Angular内置的inject函数获取服务实例,函数会自动关联当前注入上下文:
// eslint-disable-next-line @typescript-eslint/ban-types import { inject } from '@angular/core'; import { FeatureManagerService } from './feature-manager.service'; export function feature(flag: string) { return function (target: Object, key: string | symbol) { Object.defineProperty(target, key, { configurable: false, enumerable: false, get: () => { const featureService = inject(FeatureManagerService); return featureService.isFeatureEnabled(flag); }, set: () => { throw new Error('Feature flag property is read-only'); } }) } }
方式B:兼容低版本Angular
先在main.ts中将根注入器挂载到全局变量:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; platformBrowserDynamic().bootstrapModule(AppModule) .then(moduleRef => { // 挂载根注入器到全局 (window as any).ngRootInjector = moduleRef.injector; }) .catch(err => console.error(err));
再修改装饰器代码:
// eslint-disable-next-line @typescript-eslint/ban-types import { FeatureManagerService } from './feature-manager.service'; export function feature(flag: string) { return function (target: Object, key: string | symbol) { Object.defineProperty(target, key, { configurable: false, enumerable: false, get: () => { const featureService = (window as any).ngRootInjector.get(FeatureManagerService); return featureService.isFeatureEnabled(flag); }, set: () => { throw new Error('Feature flag property is read-only'); } }) } }
扩展说明
如果不想阻塞应用启动,可修改getter返回Observable<boolean>,配合areFlagsAvailable$判断加载状态后再返回开关值:
get: () => { const featureService = inject(FeatureManagerService); return featureService.areFlagsAvailable$.pipe( filter(available => available), map(() => featureService.isFeatureEnabled(flag)) ) }
内容的提问来源于stack exchange,提问作者Ankit Tanna
相关产品推荐
相关产品推荐

