Runtime Configuration In Angular
The majority of apps require some kind of Run-time configuration data, which they must load initially. For instance, if your app wants data, it must be aware of the REST endpoints' primary location. Additionally, endpoints in the development, testing, and production environments may differ. The APP INITIALIZER is used in this article to read the configuration file in Angular.
Where to Store configuration
There are environment variables in Angular where you can store runtime parameters, but they have restrictions. Environment variable settings are made at build time and cannot be altered at run time.
The configuration data can be stored in a database. But in order to connect to the database, we still need to store the REST endpoints.
The proper course of action is to keep the configuration data in a secured area in a config file. The configuration file will be released simultaneously with the application. When the application loads, the configuration may be loaded from it.
We'll keep it in the src/app/assets/config folder for the examples in this article.
The configuration can be saved in any format. The preferred option is either
{ "appTitle": "APP_INITIALIZER Example App", "apiServer" : { "link1" :"http://amazon.com", "link2" :"http://ebay.com" }, "appSetting" : { "config1" : "Value1", "config2" : "Value2", "config3" : "Value3", "config3" : "Value4" } }
When to read the configuration
APP_INITIALIZER
import { NgModule, APP_INITIALIZER } from '@angular/core';
The next step is to construct a service that will read the configuration file. In the example below, AppConfigService loads the configuration using its load method.
@Injectable() export class AppConfigService { constructor(private http: HttpClient) {} load() { //Read Configuration here } }
Create a factory method that calls the AppConfigService's load function next. The factory method needs to have the appConfigService injected, as seen below.
export function initializeApp(appConfigService: AppConfigService) { return () => appConfigService.load(); }
In order to give the initializeApp using the useFactory, useing the APP INITIALIZER token. Since the initializeApp requires the AppConfigService, don't forget to add it as a dependency using the deps. We can add more than one provider to the APP INITIALIZER token by using the multi: true option.
providers: [ AppConfigService, { provide: APP_INITIALIZER,useFactory: initializeApp, deps: [AppConfigService], multi: true} ],
Reading the Configuration file
load() { const jsonFile = `assets/config/config.json`; return new Promise<void>((resolve, reject) => { this.http.get(jsonFile).toPromise().then((response : IAppConfig) => { AppConfigService.settings = <IAppConfig>response; console.log( AppConfigService.settings); resolve(); //Return Sucess }).catch((response: any) => { reject(`Failed to load the config file`); }); }); }
Example
Create the Config file
export interface IAppConfig { env: { name: string } apiServer: { link1:string, link2:string, } }
Then, as seen below, build the actual configuration file in assets/config/config.json.
{ "env": { "name":"Dev" }, "apiServer" : { "link1" :"http://amazon.com", "link2" :"http://ebay.com" } }
Service
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable() export class AppConfigService { static settings: IAppConfig; constructor(private http: HttpClient) {} load() { const jsonFile = `assets/config/config.json`; return new Promise<void>((resolve, reject) => { this.http.get(jsonFile).toPromise().then((response : IAppConfig) => { AppConfigService.settings = <IAppConfig>response; console.log('Config Loaded'); console.log( AppConfigService.settings); resolve(); }).catch((response: any) => { reject(`Could not load the config file`); }); }); } }
Create static settings variable
static settings: IAppConfig;
HttpClient is then injected into the constructor. To read the configuration file, we employ the HTTP get the technique.
constructor(private http: HttpClient) {}
The load method assigns the configuration file's location to the jsonFile constant.
const jsonFile = assets/config/config.json;
Then, we return the Promise
return new Promise<void>((resolve, reject) => {
We send a GET request to the configuration file from within the Promise. The response that is given back is assigned to the IAppConfig interface.
this.http.get(jsonFile).toPromise().then((response : IAppConfig) => {
Put it in the settings field. It is a static variable, so take note. So, we're using AppConfigService.settings in this situation.
AppConfigService.settings = <IAppConfig>response;
Output the values to the console
console.log('Config Loaded'); console.log( AppConfigService.settings);
And finally, make a decision to keep your promise.
resolve();
In addition, catch any problems and reject the promise. The application will no longer load thanks to Angular.
.catch((response: any) => { reject(`Could not load the config file`); });
Loading the Runtime configuration
import { BrowserModule } from '@angular/platform-browser'; import { NgModule, APP_INITIALIZER } from '@angular/core'; import { HttpModule } from '@angular/http'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { AboutUsComponent, ContactUsComponent, HomeComponent} from './pages' import { AppConfigService } from './app-config.service'; import { HttpClientModule } from '@angular/common/http'; export function initializeApp(appConfigService: AppConfigService) { return (): Promise<any> => { return appConfigService.load(); } } @NgModule({ declarations: [ AppComponent, AboutUsComponent,HomeComponent,ContactUsComponent ], imports: [ HttpClientModule, BrowserModule, AppRoutingModule, ], providers: [ AppConfigService, { provide: APP_INITIALIZER,useFactory: initializeApp, deps: [AppConfigService], multi: true} ], bootstrap: [AppComponent] }) export class AppModule { }
We must import APP INITIALIZER from @angular/core first.
import { NgModule, APP_INITIALIZER } from '@angular/core';
Next, import HttpClientModule and AppConfigService
import { AppConfigService } from './app-config.service'; import { HttpClientModule } from '@angular/common/http';
AppConfigService loads the configuration for us. We now require a function to call the load method. As a result, we will write the initializeApp function, which calls the appConfigService. load() method
export function initializeApp(appConfigService: AppConfigService) { return (): Promise<any> => { return appConfigService.load(); } }
Finally, we must instruct angular to run initializeApp when an application launches. As seen below, we accomplish this by adding it to the provider's array using the APP INITIALIZER token.
providers: [ AppConfigService, { provide: APP_INITIALIZER,useFactory: initializeApp, deps: [AppConfigService], multi: true} ],
Read the configuration in components
import { Component } from '@angular/core'; import { AppConfigService} from '../../app-config.service'; @Component({ template: `About Us`, }) export class AboutUsComponent { protected apiServer = AppConfigService.settings.apiServer; constructor() { console.log(this.apiServer.link1); console.log(this.apiServer.link2); } }
the component/service must first import the AppConfigService.
import { AppConfigService} from '../../app-config.service';