Angular 模块导入的动态配置

Dynamic configuration for Angular module imports

我们的 Angular 12 应用程序有一个模块,该模块导入我们要根据配置文件配置的依赖项,仅在运行时可用,不是在编译时。

有问题的包是 ng-intercom,尽管我想以后其他包也会出现同样的问题。

动态配置背后的动机是我们的应用程序在 4 个不同的环境中运行,我们不想为每个创建单独的构建,因为它们之间的唯一区别是包含后端 URL 和一些应用程序 ID(如 Intercom、Facebook 应用程序 ID 等)

目前有问题的导入是这样制作的:

imports: [
  ...
  IntercomModule.forRoot({
    appId: env.intercomID,
    updateOnRouterChange: true,
  }),
  ...

问题是appID 应该是可配置的,env 变量应该动态加载。目前,它是 JSON 导入并编译到代码中,但这意味着我们不能在不为每个环境重建代码的情况下为不同的环境更改它:

import env from '../../assets/environment.json';

我们有一个 APP_INITIALIZER,但是,这不会阻止模块在解决之前被导入:

{
  provide: APP_INITIALIZER,
  useFactory: AppService.load,
  deps: [AppService],
  multi: true,
},

...和相关的配置加载器:

static load(): Promise<void> {
  return import('../../assets/environment.json').then((configuration) => {
    AppService.configSettings = configuration;
  });
}

我们可以使用此配置而不会对我们的组件和服务造成问题。

我们设法在 angularx-social-login 的配置中实现了我们想要的结果:

providers: [
  ...
  {
    provide: 'SocialAuthServiceConfig',
    useValue: new Promise(async resolve => {
      const config = await AppService.config();
      resolve({
        autoLogin: true,
        providers: [
          {
            id: FacebookLoginProvider.PROVIDER_ID,
            provider: new FacebookLoginProvider(config.facebookApi),
          }
        ]
    } as SocialAuthServiceConfig);
  ...
]

SocialAuthServiceConfig 是一个提供者,但是我们找不到类似的方法来配置 ng-intercom 的导入。

我们能以某种方式实现吗? 有没有办法动态配置模块导入?

我认为没有必要动态配置模块导入来实现这一点,相反,您可以执行以下操作:

  • 导入没有 forRoot 函数的 IntercomModule
  • 提供 IntercomConfig class 使用 useFactory 函数从 AppService.configSettings:
  • 读取数据配置
  providers: [
    {
      provide: IntercomConfig,
      useFactory: intercomConfigFactory,
    },
  ],

// ....

export function intercomConfigFactory(): IntercomConfig {
  return {
    appId: AppService.configSettings.intercomAppId,
    updateOnRouterChange: true,
  };
}

大多数库作者提供了一种稍后初始化库的方法。 forRoot 调用可以伪造。如果你需要配置对讲,你仍然可以调用forRoot但是你可以使用空id:

  IntercomModule.forRoot({
    appId: null,
    updateOnRouterChange: true,
  }),

然后你可以用 app_id 调用 boot 然后使用。

 // AppComponent 
 constructor(private appService: AppsService, private intercom: Intercom) {
    this.startIntercom();
 }

 private async startIntercom() {
    const config = this.appService.config();
    this.intercom.boot({app_id: config.intercom_app_id});
 }

总的来说,你可以通过阅读库源代码学到很多东西。 大多数库提供类似于 intercom.boot.

的方法