Social media links

Angular: dynamically load config file before app starts

There are multiple ways to load different configuration files in your Angular application, depending on the environment you are targeting. One way is to create different environments files environment.test.ts, environment.prod.ts, environment.<YOUR_ENV>.ts, but that would require to create as many builds as you have environments where you want to deploy your app.

In this post, I’ll describe how to fetch a configuration file before the Angular application starts, with only one identical built artifact, using APP_INITIALIZER (https://angular.io/api/core/APP_INITIALIZER).

1) Create your config file

The first step is to create a configuration file that will be hosted in the same environment where your angular application will be deployed. E.g:

app.config.json

{
  "version": "1.0.0",
  "apiEndpoint":"/api/"
}

2) Create the configuration service

We then need to create a service that will be in charge of loading and storing the values from the config file.

This is a basic service; the properties have the same name as the one in the config file, and it contains a load() method that should return a Promise.

This method does an http call to fetch your config file, convert it to a Promise with toPromise() and map the config data from the JSON to our class properties in the promise resolution Object.assign(this, data)

app.config.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
    providedIn: 'root'
})
export class AppConfigService {
  public version: string;
  public apiEndpoint: string;

	constructor(private http: HttpClient) {}

		load() :Promise<any>  {

      const promise = this.http.get('/app.config.json')
        .toPromise()
        .then(data => {
          Object.assign(this, data);
          return data;
        });

      return promise;
  }
}

3) APP_INITIALIZER

The last step is to call our load() method from our newly created service in the application start.

We need to create a factory function that return a promise in our main module app.module.ts

export function appConfigInit(appConfigService: AppConfigService) {
  return () => {
    return appConfigService.load()
  };
}

We then add an entry in the module providers array using the APP_INITIALIZER DI token. It is used to provide one or more initialization functions, ensuring that our config load is called before the application starts.

providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: appConfigInit,
      multi: true,
      deps: [AppConfigService]
    }
  ]

Complete app.module.ts :

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { AppConfigService } from '@app/core/services/app-config.service';
import { AppComponent } from './app.component';

export function appConfigInit(appConfigService: AppConfigService) {
  return () => {
    return appConfigService.load()
  };
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: appConfigInit,
      multi: true,
      deps: [AppConfigService]
    }
  ],
  bootstrap: [AppComponent]
})

export class AppModule { }

Using the configuration values

To use the config, simply import your service into your other services or components.

version.service.ts

import { AppConfigService } from './app-config.service';

@Injectable()
export class VersionService{

  constructor(private config: AppConfigService) { }

	getVersion(): string {
		return this.config.version;
	}
}