How to use Angular Dynamic Configuration

Angular applications by default support compile time configurations. But there are situations that we have to use dynamic configurations. This article will show you how to use dynamic configurations in angular applications.
Angular applications by default support compile time configurations. But there are situations that we have to use dynamic configurations. This article will show you how to use dynamic configurations in angular applications.

Angular configurations

Compile time configuration

Angular applications out of the box support compile time configurations. You can find environment.ts and environment.prod.ts files inside the environments folder. All the configurations in these files will be included to angular application when you compile the application. When you compile the application using production mode, it will use configurations in environment.prod.ts file.

Dynamic configuration

Like we mentioned in the previous section, angular out of the box options to use configuration will work only in compile time. However there are situations that we have to change the configuration in deployment time or even after that. Unfortunatly angular environment.ts files wont work in that way.

How to use dynamic configurations in angular applications

In order to use dynamic configurations, we will use following steps
  • Use a json file to store configurations
  • Read the json file content
  • Use configurations

Use a json file to store configurations

Let's create a simple json file with the configuration we want. For this article we will store this json file inside the assert folder
{
    "sampleConfigValue": "this is a sample value from config"
}

Read the json file content

Now we have the json file and lets create an interface to represent the json file

Use following code to generate the interface
ng g interface config/configuration
Let's update the interface to match with the properties in our json file. So it will look like following
export interface Configuration {
    sampleConfigValue:string;
}
let's create angular service to read the json file content Use following code to generate the interface
ng g service config/configuration
Update the service as following
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Configuration } from './configuration';
@Injectable({
  providedIn: 'root'
})
export class ConfigurationService {
  private configData: Configuration | undefined;
  private readonly configPath: string = '/assets/config/configuration.json';
  constructor(
    private http: HttpClient
  ) { }

  async loadConfiguration(): Promise {
    try {
      const response = await this.http.get(`${this.configPath}`)
        .toPromise();
      this.configData = response;
      return this.configData;
    } catch (err) {
      return Promise.reject(err);
    }
  }

  get config(): Configuration | undefined {
    return this.configData;
  }
}
Here you will notice that we have a loadConfiguration function which send http request and store data in a local variable. And after that we can consume that data using config property without sending another http request.

We can use this service as any other angular service and consume the content of the json. But since this is a spacial scenario, we need to load the data before application initialize.

In order to do that, we need to register this in app.module
import { BrowserModule } from '@angular/platform-browser';
import { APP_INITIALIZER, Injector, NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { Configuration } from './config/configuration';
import { ConfigurationService } from './config/configuration.service';
import { HttpClientModule } from '@angular/common/http';

export function ConfigLoader(injector: Injector): () => Promise {
  return () => injector.get(ConfigurationService).loadConfiguration();
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    AppRoutingModule
  ],
  providers: [{
    provide: APP_INITIALIZER,
    useFactory: ConfigLoader,
    deps: [Injector],
    multi: true
  }],
  bootstrap: [AppComponent]
})
export class AppModule { }
There are two things to consider here,
  • Since we are sending http request in our service, we need to import HttpClientModule
  • Using APP_INITIALIZER DI token
    The provided functions are injected at application startup and executed during app initialization. If any of these functions returns a Promise, initialization does not complete until the Promise is resolved.
    Here we are calling the service and store result for later uasage.

Use configurations

Now we can inject the service as usual angular service and consume the data
import { Component } from '@angular/core';
import { ConfigurationService } from './config/configuration.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  sampleConfigValue: string | undefined;
  title = 'angular-dynamic-configuration';
  constructor(configService: ConfigurationService) {
    this.sampleConfigValue = configService.config?.sampleConfigValue;
  }
}
you can use the value in html
<ng-container *ngIf="sampleConfigValue">
  {{sampleConfigValue}}
</ng-container>

That's all for today and see you soon in another very exiting tutorial