TL;DR – Learn how to read data from external files in Angular production builds. A step-by-step guide for developers.
This article will show you how you can read data from your external files from the production build.
Cover Image credits: Image by Author prepared with PowerPoint
Issue 1: Update multiple client projects by replacing variables in the main.**.js file
On your production build of Angular project, If You want to read data from the external configuration file (let’s say JSON format), to avoid the building process for all the time while upgrading the same code everywhere, on each server. You can easily create the JSON file and update the values only when needed, it will dynamically read your latest changes. Isn’t cool?
Issue 2: If You have to update labels everywhere in your entire application, but you hardcoded it and so making multiple changes everywhere, is much time-consuming.
What if you put everything in your JSON file or constant file? If you want to update those labels to English, Spanish, German, French, or any language, you need to update everything in your entire application.
Issue 3: Managing Local, Dev, Test, Beta, and Prod environments
In your large-scale applications, it might be hard to manage all the environments you can use, Angular + java, Angular+ node, Angular + PHP, Angular + Ruby, etc combinations for frontend backend.
Maintaining IP addresses is sometimes a headache if you have several projects and deployments!
Don't Miss: Clear Cache Programmatically in Angular Index.html: A Programmatically Simple Solution
All in One Solution
Keep a configuration file in your assets dir of your Angular project and read that configuration file before the application starts up. This solution will work for all the angular 2+ versions, it will work like a charm.
You can use it to read configuration files as you do in other languages like Java, Php, Ruby, Python, etc.
How it will load the data? — Check the procedure
a) It will read an environment file named ‘environment.json’. This file indicates what is the current working environment. Options are: ‘development’ and ‘production’;
b) It will read a config JSON file based on what is found in the environment file. If the environment is “production”, the file is ‘config.production.json’. If the environment is “development”, the file is ‘config.development.json’.
All these read things will be done before Angular starts up the application.
It assumes you already have a working application, with a module and everything set up. Follow the below steps: —
Update your Existing Module
Open your existing module (let’s say app.module.ts) and add the following couple of lines to your list of providers.
import { APP_INITIALIZER } from '@angular/core'; import { AppConfig } from './app.config'; import { HttpModule } from '@angular/http'; ... //Add this function as initiating load method first function initConfig(config: AppConfig){ return () => config.load() } @NgModule({ imports: [ ... HttpModule ], ... providers: [ ... AppConfig, { provide: APP_INITIALIZER, useFactory: initConfig, deps: [AppConfig], multi: true } ], ... }); export class AppModule { ... }
- The first line makes
AppConfig
class available to Angular. - The second line uses
APP_INITIALIZER
to executeConfig.load()
the method before application startup. The ‘multi: true
’ is being used because an application can have more than one line ofAPP_INITIALIZER
. - Make sure you set “
HttpModule
” in “imports
” section if you want to make HTTP calls using Angular built-inHttp
library.
Setup app.config.ts
Create a class AppConfig and name the file ‘app.config.ts’ (It doesn’t matter what file name you keep for the same).
This is the place, we will do the reading of the environment and config files. The data of both files will be stored in the class so we can retrieve them later.
Note that, native Angular Http
the library is used to read the JSON files.
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 environment: 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 (environment file) */ public getEnv(key: any) { return this.environment[key]; } /** * This method: * a) Loads "environment.json" to get the current working environment (e.g.: 'production', 'development') * b) Loads "config.[environment].json" to get all environment's variables (e.g.: 'config.development.json') */ public load() { return new Promise((resolve, reject) => { this.http.get('environment.json').map( res => res.json() ).catch((error: any):any => { console.log('Configuration file "environment.json" could not be read'); resolve(true); return Observable.throw(error.json().error || 'Server error'); }).subscribe( (envResponse) => { this.environment= envResponse; let request:any = null; switch (envResponse.environment) { case 'production': { request = this.http.get('config.' + envResponse.environment+ '.json'); } break; case 'development': { request = this.http.get('config.' + envResponse.environment+ '.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.environment+ ' configuration file'); resolve(error); return Observable.throw(error.json().error || 'Server error'); }) .subscribe((responseData) => { this.config = responseData; resolve(true); }); } else { console.error('environmentconfig file "environment.json" is not valid'); resolve(true); } }); }); } }
Why use Promises? — See that, resolve()
is used in all scenarios because we don’t want the application to crash if any problem is found in the configuration files. If you prefer, you can set error scenarios to reject()
.
If you want you can use Observable
from Rxjs
too. (Recommended for Angular 5+ versions)
Create environment.json
This is the place, you will configure the current development environment. Allowed values are ‘development’ and ‘production’.
{
"env": "development"
}
or
{
"env": "production"
}
You may add this file to .gitignore
your convenience.
Setup your config.development.json
This is the place you will configure development config variables. You can add as many variables as you want to this JSON
file.
{
"host": "localhost"
}
You may add this file to .gitignore
to your convenience.
Set up config.production.json
This is the place you will write production config variables. You can add as many variables as you want to this JSON
file.
{
"host": "192.168.10.142"
}
You may add this file to .gitignore
to your convenience.
Access values in Any Angular Component
Example of how you can read the values previously loaded from both files. In this case, we are reading the ‘host’ variable from the config file and ‘environment’ from the environment.json
file.
import { AppConfig } from './app.config'; export class AnyClass { constructor(private config: AppConfig) { // note that AppConfig is injected into a private property of AnyClass } myMethodToGetHost() { // will print 'localhost' let host:string = config.get('host'); } myMethodToGetCurrentEnv() { // will print 'development' let env: string = config.getEnv('environment'); } }
Bingo, You did it! If you make the prod build using ng build --prod
command, then it works fantastically with charm.
If you find this article helpful, Show some love with upvotes, and share it with your friends.
Discover more from 9Mood
Subscribe to get the latest posts sent to your email.
0 Comments