Nuestra aplicación Angular 12 tiene un módulo que importa una dependencia que queremos configurar en función de un archivo de configuración que solo está disponible en tiempo de ejecución , no en tiempo de compilación.
El paquete en cuestión es ng-intercom , aunque imagino que el mismo problema podría surgir más adelante con otros paquetes también.
La motivación detrás de la configuración dinámica es que nuestra aplicación se ejecuta en 4 entornos diferentes y no queremos crear compilaciones separadas para cada uno, ya que la única diferencia entre ellas es el archivo de configuración que contiene la URL del servidor y algunas ID de aplicación (como intercomunicador, ID de la aplicación de Facebook, etc.)
Así es como se realiza actualmente la importación en cuestión:
imports: [ ... IntercomModule.forRoot({ appId: env.intercomID, updateOnRouterChange: true, }), ...El problema es que appID debe ser configurable, la variable env debe cargarse dinámicamente . Actualmente, es un JSON importado y compilado en el código, pero eso significa que no podemos cambiarlo para nuestros diferentes entornos sin reconstruir el código para cada uno:
import env from '../../assets/environment.json';Sin embargo, tenemos un APP_INITIALIZER que no impide que los módulos se importen antes de que se resuelva:
{ provide: APP_INITIALIZER, useFactory: AppService.load, deps: [AppService], multi: true, },... y el cargador de configuración relevante:
static load(): Promise<void> { return import('../../assets/environment.json').then((configuration) => { AppService.configSettings = configuration; }); }Podemos usar esta configuración sin problemas con nuestros componentes y servicios.
Logramos lograr los resultados que queremos en la configuración de 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 es un proveedor, sin embargo, no pudimos encontrar una manera de configurar la importación de ng-intercom de manera similar.
¿Podemos lograr esto de alguna manera? ¿Hay alguna manera de configurar dinámicamente las importaciones de módulos?
Creo que no hay necesidad de configurar las importaciones de módulos dinámicamente para lograr eso, en su lugar, puede hacer lo siguiente:
IntercomModule sin la función forRoot .IntercomConfig usando la función useFactory que lee la configuración de datos de AppService.configSettings : providers: [ { provide: IntercomConfig, useFactory: intercomConfigFactory, }, ], // .... export function intercomConfigFactory(): IntercomConfig { return { appId: AppService.configSettings.intercomAppId, updateOnRouterChange: true, }; }la mayoría de los autores de bibliotecas proporcionan una forma de inicializar la biblioteca más tarde. La llamada forRoot se puede falsificar. Si necesita configurar el intercomunicador, aún puede llamar a forRoot pero puede usar una identificación vacía:
IntercomModule.forRoot({ appId: null, updateOnRouterChange: true, }), Luego puede llamar a boot con app_id que luego se usa.
// 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}); } En general, puede aprender mucho leyendo el código fuente de la biblioteca. La mayoría de las bibliotecas proporcionan métodos similares a intercom.boot .