Runtime Configuration In Angular

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

Before we load our first page, some configuration data is required. The configuration should therefore be read very early in the application. When the application launches, Angular performs the injection token with the name APP INITIALIZER.

APP_INITIALIZER

The preconfigured injection token offered by Angular is called APP INITIALIZER. The Angular will execute the function provided by this token when the application loads. Angular will wait until the promise is resolved if the function returns a promise. Because of this, it will be the perfect location to read the settings and execute some initialization logic before the application is started.

We must import APP INITIALIZER into our Root Module before using it.

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

We must send an HTTP GET request and return a Promise in order to read the configuration or 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 a new Angular application.

Create the Config file

We will use the JSON format for our settings.

First, We will develop an Interface IAppConfig

Create the app-config.service.ts in the src/app folder and create IAppConfig as shown below.

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

The service's job is to store the configuration by sending an HTTP GET request to the config.json file.

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

Next, inject the Service into the AppModule.

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}
],

InitializeApp is a function, not a class, hence the useFactory is used.

To inform Angular that initializeApp depends on AppConfigService, we use the deps:[AppConfigService] flag.

The multi-provider DI token is created when multi: true is set. A multi-provider token is the APP INITIALIZER. For APP INITIALIZER, many Providers can be defined. Each of them is called by the Angular Injector in the order that they appear in the Providers array. 

Read the configuration in components

How to read the component's runtime settings is demonstrated in the following AboutUsComponent.

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

the AppConfigService.settings reference next.

Runtime Configuration Example In Angular

Conclusion

We learned how to construct runtime configuration in this post and using it in Angular applications. We hook into Angular's initialization phase and read the configuration file using the injection token APP INITIALIZER that Angular provides. The config file is located in the src/app/assets/config folder and is in JSON format. To retrieve the data from the configuration file, we send an HTTP GET request.

I hope this article helps you and you will like it.👍

If you have any doubt or confusion then free to ask in the comment section.

Post a Comment

Previous Post Next Post