Search code examples
angularobservableruntime-configuration

how to set configuration settings in the config servcie using Angular’s APP_INITIALIZER


In my angular app, I want to use runtime configuration (to use same build artifact for any target env) and load settings from the back .net API. I have the following api url in the config.json in the assets folder.

{
  "apiUrl": "https://localhost:xxx"
}

and the configuration service that will call the back end api to get all the evnironment settings looks like below

export class ConfigService {
config: Config;
private configEndPoint = '/config';
private environmentSettings: EnvironmentSettings; 

constructor(private http: HttpClient) { }

loadConfig(): Observable<EnvironmentSettings> {
  return this.http
  .get<Config>('./assets/config.json')
  .pipe(
    switchMap(config => {
      return this.http.get<EnvironmentSettings>(config.apiUrl + this.configEndPoint)
    }))

  }

 getEnvironmentSettings(){
   return this.environmentSettings;
 }

}

In the app module I have createf factory function for app intialiser like below

export const configFactory = (configService: ConfigService) => {
  return () => configService.loadConfig();
 };

and the providers array includes

providers: [
{
  provide: APP_INITIALIZER,
  useFactory: configFactory,
  deps: [ConfigService],
  multi: true
},

The problem I have is I'm not able to set the value for enviornement settings in the load config. The loadConfig method is called by the factory function and call is made to the back end api. In the angular app, I want to inject ConfigService in components and call the getEnvironmentSettings of ConfigService to get all the settings but not sure how this can be set in the above scenario?


Solution

  • In order to make the value of environmentSettings available, you have to set the value of it with the RxJS tap operator. With the tap operator you can create side-effects for observables.

    In your case the side-effect will be setting the environmentSettings variable.

    export class ConfigService {
    config: Config;
    private configEndPoint = '/config';
    private environmentSettings: EnvironmentSettings; 
    
    constructor(private http: HttpClient) { }
    
    loadConfig(): Observable<EnvironmentSettings> {
      return this.http
      .get<Config>('./assets/config.json')
      .pipe(
        switchMap(config => {
          return this.http.get<EnvironmentSettings>(config.apiUrl + 
         this.configEndPoint)
        }),
        //variable 'settingsFromEndpoint' will contain the value from the http request
        tap((settingsFromEndpoint) => (this.environmentSettings = settingsFromEndpoint))
       )
      }
    
     getEnvironmentSettings(){
       return this.environmentSettings;
     }
    

    With this setup your configFactory will be called, which will instantiate your ConfigService and call the method loadConfig. Since the return type of that method is an observable Angular will wait for the observable to emit its first value. As soon as the first value is emitted the callback of the tap operator is called and your variable environmentSettings will be set to the result of the endpoint.