Как мне сослаться на мою конфигурацию в моем импорте?

Я добавил в свой проект AngularFire. В его ридми мне было сказано добавитьAngularFireModule.initializeApp(...) к импорту моего AppModule

Однако в примерах люди используют константу среды, это не вариант, потому что для развертывания требуется файл конфигурации, в котором он будет заменять переменные при развертывании в зависимости от среды, в которой развертывается.

Я считаю, что моя конфигурация загружается после импорта, что означает, что ничего не передается в .initializeApp(...)

app.module.ts

import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
...

export function initializeApp(appConfig: AppConfig) {
  return () => appConfig.load();
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...,
    AngularFireModule.initializeApp(AppConfig.firebase),
    AngularFireAuthModule
  ],
  providers: [
    AppConfig,
    { provide: APP_INITIALIZER, useFactory: initializeApp, deps: [AppConfig], multi: true },
    ...,
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

app-config.ts

import { Injectable } from '@angular/core';
import { AppConfigModel } from './shared/models/app-config.model';
import { HttpClient } from '@angular/common/http';
import { environment } from '../environments/environment';

/**
 * This config pattern based off https://devblogs.microsoft.com/premier-developer/angular-how-to-editable-config-files/
 * which is referenced in Octopus Deploy blog here https://octopus.com/blog/javascript-configuration
 */
@Injectable()
export class AppConfig extends AppConfigModel {

  constructor(private http: HttpClient) {
    super();
  }

  /**
   * Loads the required config file from assets, depending on configName of environment.
   * This allows replacement of variables by Octopus Deploy
   */
  public load() {
    const jsonFile = `assets/config/config.${environment.configName}.json`;

    return new Promise<void>((resolve, reject) => {
      this.http.get(jsonFile).toPromise().then((response: AppConfigModel) => {
        this.mapConfigToProperties(response);
        resolve();
      }).catch((response: any) => {
        console.error('On config load', response);
        reject(`Could not config load file '${jsonFile}': ${JSON.stringify(response)}`);
      });
    });
  }

  private mapConfigToProperties(config: any) {
    Object.keys(config).forEach(key => {
      AppConfig[key] = config[key];
    });
  }
}

Я бы очень хотел, чтобы эта проблема была решена без необходимости изменять какие-либо процессы сборки и просто иметь возможность использовать AppConfig чтобы получить конфигурацию.

package.json

{
  ...,
  "dependencies": {
    "@angular/animations": "^6.1.10",
    "@angular/cdk": "^6.3.0",
    "@angular/common": "^6.0.3",
    "@angular/compiler": "^6.0.3",
    "@angular/core": "^6.0.3",
    "@angular/fire": "^5.3.0",
    "@angular/forms": "^6.0.3",
    "@angular/http": "^6.0.3",
    "@angular/platform-browser": "^6.0.3",
    "@angular/platform-browser-dynamic": "^6.0.3",
    "@angular/router": "^6.0.3",
    "core-js": "^2.5.4",
    "firebase": "^7.7.0",
    "moment": "^2.24.0",
    "ng-pick-datetime": "^6.0.16",
    "ng-pick-datetime-moment": "1.0.7",
    "ngx-image-cropper": "^2.0.2",
    "ngx-smart-modal": "^7.1.1",
    "ngx-textarea-autosize": "^2.0.3",
    "ngx-toastr": "^10.0.4",
    "rxjs": "6.3.3",
    "zone.js": "^0.8.26"
  },
  ...
}

main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { FirebaseOptionsToken, AngularFireModule } from '@angular/fire';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.log(err));

1 ответ

Этого можно добиться, задав параметры для platformBrowserDynamic(...) который принимает массив поставщиков, аналогичный массиву в providers свойство @NgModule.

Мы решим эту проблему с помощью этого параметра. Мы введем параметр для конфигурации firebase, а затем удалим его из импорта в AppModule.

main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { FirebaseOptionsToken, AngularFireModule } from '@angular/fire';

if (environment.production) {
  enableProdMode();
}

// Get the config file
fetch(`assets/config/config.${environment.configName}.json`)
  .then(response => response.json())
  .then((response: any) => {
    // Here we add the additional provider
    platformBrowserDynamic([{provide: FirebaseOptionsToken, useValue: response.firebaseSettings}])
      .bootstrapModule(AppModule)
      .catch(err => console.log(err));
}).catch((response: any) => {
  console.error('On config load', response);
});

app.module.ts

import { AngularFireModule, FirebaseOptionsToken } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
...

export function initializeApp(appConfig: AppConfig) {
  return () => appConfig.load();
}

// initialize app with empty config
const angularFireImport = AngularFireModule.initializeApp({});

// remove the provider that we have provided for in the platforBrowserDynamic
angularFireImport.providers = angularFireImport.providers.filter(
  (provider: any) => provider.provide !== FirebaseOptionsToken
);


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...,
    // import the module we constructed above without the provider
    angularFireImport,
    AngularFireAuthModule
  ],
  providers: [
    ...,
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Этот ответ был вдохновлен запуском приложения AngularFire2 в конфликтах модулей с данными динамической конфигурации

Дополнительное чтение о провайдерах и внедрении угловых зависимостей, ссылка

Другие вопросы по тегам