动态 module/service 配置和 AOT

Dynamic module/service configuration and AOT

我需要根据运行时开关动态配置一些 Angular 服务。在 AOT 之前的几天,我使用以下代码让它工作:

@NgModule({
  imports: [HttpModule],
  providers: []
})
export class MyModule {
  static forRoot(config: MyConfiguration): ModuleWithProviders {
    return {
      ngModule: MyModule,
      providers: [
        SomeService,
        {
          provide: SomeOtherService,
          useFactory: (some: SomeService, http: Http) => {
            switch (config.type) {
              case 'cloud':
                return new SomeOtherService(new SomethingSpecificForCloud());
              case 'server':
                return new SomeOtherService(new SomethingSpecificForServer());
            }
          },
          deps: [SomeService, Http]
        },

      ]
    };
  }
}

然后在我的 AppModule 中,我会将其导入为 MyModule.forRoot(myConfig)

当我更新 CLI 和 Angular 时,它不再编译,因为它无法进行静态分析。我明白为什么,但我仍然不确定正确的解决方法是什么。

我是否滥用了这种 forRoot() 方法?您如何编写模块以便根据运行时开关产生不同的服务?

我找到了一种实现方法:通过提供程序公开配置,然后注入 "static" 工厂函数。上面的代码看起来像这样:

// Necessary if MyConfiguration is an interface
export const MY_CONFIG = new OpaqueToken('my.config');

// Static factory function
export function someOtherServiceFactory(config: MyConfiguration,some: SomeService, http: Http) {
  switch (config.type) {
    case 'cloud':
      return new SomeOtherService(new SomethingSpecificForCloud());
    case 'server':
      return new SomeOtherService(new SomethingSpecificForServer());
  }
}

@NgModule({
  imports: [HttpModule],
  providers: []
})
export class MyModule {
  static forRoot(config: MyConfiguration): ModuleWithProviders {
    return {
      ngModule: MyModule,
      providers: [
        SomeService,
        { provide: MY_CONFIG, useValue: config },
        {
          provide: SomeOtherService,
          useFactory: someOtherServiceFactory,
          deps: [MY_CONFIG, SomeService, Http]
        },

      ]
    };
  }
}

一切正常,但我仍然很想知道这是否真的是个好主意,或者我是否做错了一些事情并且应该采取完全不同的方法来解决这个问题。


我找到了另一个解决方案:

  1. 使用 Angular CLI 环境。
  2. 为具有不同实现或依赖于不同环境的服务创建一个抽象classes/interfaces。
  3. 从每个环境文件中导出正确的类型(谁说它必须只是一个普通的 JS 对象?)。
  4. 在模块提供者定义中,从环境导入
  5. 在编译时,CLI 环境将使正确的东西得到链接。

更多信息和示例项目在 my blog