You need to enable JavaScript to run this app. 如何在Angular自定义装饰器中注入并调用单例服务
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.11 15:18:04