If you are experienced with Java or any other backend development ecosystems, there are chances that you’ve worked with
loading data from an external configuration file (properties, YAML, JSON …), where we use environment specific settings like URLs, credentials etc..
And you may wonder how the same can be done in an angular app. The Angular CLI has created environment.ts
for DEV and
environment.prod.ts
for PROD and will choose the appropriate one during the build process. The usual way of doing this is
$ ng build
and for production environment,
$ ng build --env=prod
This approach has a limitation that we need to issue new builds everytime we change the environment. I was searching for a solution where I can isolate the configuration from the build process and load before the application startup.
env.json - This file specifies the environment namely development,production or any others.
{"env": "development"}
env.development.json - We can have the following file for each different environments. And the filename should follow the pattern
env.<Environment name>.json
eg: env.development.json env.production.json
{"api_root": "http://localhost:9000","users_api" : "/users",...}
Create an angular class AppConfig
where we can read the JSON files and store it for later use inside our application.
import { Inject, Injectable } from '@angular/core';import { Http } from '@angular/http';import { Observable } from 'rxjs/Rx';@Injectable()export class AppConfig {private config: Object = null;private env: Object = null;constructor(private http: Http) {}/*** Use to get the data found in the second file (config file)*/public getConfig(key: any) {return this.config[key];}/*** Use to get the data found in the first file (env file)*/public getEnv(key: any) {return this.env[key];}/*** This method:* a) Loads "env.json" to get the current working environment (e.g.: 'production', 'development')* b) Loads "config.[env].json" to get all env's variables (e.g.: 'config.development.json')*/public load() {return new Promise((resolve, reject) => {this.http.get('./assets/env/env.json').map( res => res.json() ).catch((error: any): any => {console.log('Configuration file "env.json" could not be read');resolve(true);return Observable.throw(error.json().error || 'Server error');}).subscribe( (envResponse) => {this.env = envResponse;let request: any = null;switch (envResponse.env) {case 'production': {request = this.http.get('./assets/env/env.' + envResponse.env + '.json');} break;case 'development': {request = this.http.get('./assets/env/env.' + envResponse.env + '.json');} break;case 'default': {console.error('Environment file is not set or invalid');resolve(true);} break;}if (request) {request.map( res => res.json() ).catch((error: any) => {console.error('Error reading ' + envResponse.env + ' configuration file');resolve(error);return Observable.throw(error.json().error || 'Server error');}).subscribe((responseData: any) => {this.config = responseData;resolve(true);});} else {console.error('Env config file "env.json" is not valid');resolve(true);}});});}}
We need to load the configuration before the application startup. For that lets make use of the APP_INITIALIZER
from angular.
import { APP_INITIALIZER } from '@angular/core';import {AppConfig} from './config/app.config';import {HttpModule} from '@angular/http';export function initConfig(config: AppConfig) {return () => config.load();}@NgModule({imports: [...HttpModule],...providers: [...AppConfig,{provide: APP_INITIALIZER,useFactory: initConfig,deps: [AppConfig],multi: true}],...})export class AppModule { }
We can use the getConfig()
method from AppConfig class to read values from the configuration file.
import {Http} from '@angular/http';import {Injectable} from '@angular/core';import {AppConfig} from '../config/app.config';import {Response} from '@angular/http';@Injectable()export class UserService {api_root: string;constructor(private http: Http, private config: AppConfig) {this.api_root = this.config.getConfig('api_root');}getAllUsers() {return this.http.get(this.api_root + this.config.getConfig('users_api')).subscribe((response: Response) => {console.log(response.json());},(error) => console.log(error));}}
With this approach, we can load and modify configuration without rebuilding the code.
Legal Stuff
Social Media