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;
}
}