首页 文章

带有forRoot的动态提供程序数组

提问于
浏览
2

我正在写一个angular2 InterceptorModule ,这是大核心项目的一部分 . 每个模块都需要完全可配置和独立 . 所有模块都使用 foorRoot 方法编写,使我们能够实现全局单例并将任何配置传递给它 .

我们决定在主模块的 forRoot metod中定义 HTTP_INTERCEPTORS 提供者 . 多亏了这个,我们在一个地方有一个配置,所有URL规则都可以触发特定的拦截器 .

这是主 AppModuleimport 部分中的简单用法:

CoreInterceptorModule.forRoot([
  {
    instance: TestInterceptor,
    runConditions: [],
  },
  {
    instance: MenuInterceptor,
    runConditions: [InterceptorRunConditions.WhiteList],
    whiteList: ['v1/'],
  },
  {
    instance: MenuInterceptor,
    runConditions: [
      InterceptorRunConditions.WhiteList, 
      InterceptorRunConditions.BlackList
    ],
    whiteList: ['v1/'],
    blackList: ['v1/authorize']
  },
]),

CoreStorageModule.forRoot({
  mode: StorageMode.LocalStorage,
  prefix: 'plCore',
  modesPriority: [StorageMode.SessionStorage, StorageMode.Memory],
}),

我有一个问题,从 forRoot 部分动态注入提供程序到 ModuleWithProviders ModuleWithProviders .

工作方案

这是注入 HTTP_INTERCEPTORS 的有效解决方案:

@NgModule({})
export class CoreInterceptorModule {
  static config(interceptorConfig: IInterceptorConfig[]): ModuleWithProviders {
    return {
      ngModule: CoreInterceptorModule,
      providers: [
        { provide: INTERCEPTOR_CONFIG, useValue: interceptorConfig },
        { provide: HTTP_INTERCEPTORS, useClass: interceptorConfig[0].instance, multi: true },
        { provide: HTTP_INTERCEPTORS, useClass: interceptorConfig[1].instance, multi: true },
        { provide: HTTP_INTERCEPTORS, useClass: interceptorConfig[2].instance, multi: true },
      ],
    };
  }
}

当然这个解决方案很糟糕,因为 forRoot 中定义的 HTTP_INTERCEPTORS 的数量是动态的 .

不工作的情况

解决方案就是动态注入提供者,如下所示:

@NgModule({})
export class CwaCoreInterceptorModule {
  static forRoot(interceptorConfig: IInterceptorConfig[]): ModuleWithProviders {

    // base module configuration
    const moduleWithProviders: ModuleWithProviders = {
      ngModule: CwaCoreInterceptorModule,
      providers: [
        { provide: INTERCEPTOR_CONFIG, useValue: interceptorConfig },
      ],
    };

    // update HTTP_INTERCEPTORS array
    interceptorConfig.forEach((e) => {
      moduleWithProviders.providers.push({
        provide: HTTP_INTERCEPTORS, useClass: e.instance, multi: true
      });
    });

    return moduleWithProviders;
  }
}

这段代码导致异常:

ERROR in Error during template compile of 'AppModule' Function calls are not supported in decorators but 'CoreInterceptorModule' was called.

我读了一些关于这个问题的话题,但在我的案例中没有一个有用 . 问题是在 forRoot 方法中 return 语句之前无法调用任何函数 .

问题是如何在 forRoot 方法中动态添加 HTTP_INTERCEPTORS ?我想过像 useFactory 这样的东西但它接缝只能返回一个值(一个实例) .

1 回答

  • 0

    我解决了这个问题 . 我没有使用自定义配置类型 IInterceptorConfig (后来无法迭代),而是创建了从原始 Angular\@code\classProvider 继承的自定义Angular DI提供程序类型:

    export interface InterceptorClassProvider extends ClassProvider {
      runConditions: InterceptConditions[];
      whiteList?: string[];
      blackList?: string[];
    }
    

    感谢 I have my additional fields . forRoot 方法配置如下所示:

    CwaCoreInterceptorModule.forRoot([
      {
        provide: HTTP_INTERCEPTORS,
        useClass: MenuInterceptor,
        multi: true,
        runConditions: [InterceptConditions.WhiteList],
        whiteList: ['/v1']
      },
    ]),
    

    And finally and the most important I can dynamically fill ModuleWithProviders interface in sharedModule without errors

    static forRoot(interceptorProviders: InterceptorClassProvider[]): ModuleWithProviders {
      return {
        ngModule: CwaCoreInterceptorModule,
        providers: [
          { provide: INTERCEPTOR_CONFIG, useValue: interceptorProviders },
          ...interceptorProviders
        ],
      };
    }
    

    它的工作原理是因为我使用数组扩展运算符( ... )而不是任何其他函数调用 . 这个解决方案的唯一缺点是一些不必要的属性,如 provideuseClassmulti ,它们无法封装在任何较低层 .

相关问题