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

时间:2018-04-05 06:04:04

标签: angular

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

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

这主要用于主要 AppModule import部分:

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部分的提供程序从 CoreInterceptorModule 动态注入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 },
      ],
    };
  }
}

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

不工作情况

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

@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;
  }
}

这段代码导致异常:

在AppModule'的模板编译期间出现错误错误 装饰器不支持函数调用,但是CoreInterceptorModule' 被称为。

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

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

1 个答案:

答案 0 :(得分:0)

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

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

感谢我有其他字段forRoot方法配置如下所示:

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

最后,最重要的是我可以动态填充sharedModule中的ModuleWithProviders界面而不会出现错误

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

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