How to use Angular Dynamic Configuration
Posted on January 08, 2021Angular 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 interfaceng 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.moduleimport { 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