In this post, Premier Developer consultant Laurie Atkinson walks through how to allow editing of your Angular configuration files after your app has been built, bundled, and deployed.
The Angular-CLI is the recommended way to build a production-ready app, complete with bundling, uglifying, and tree-shaking. An Angular-CLI generated application even comes with a mechanism for creating environment-specific versions. However, those configuration files are in TypeScript and do not allow editing by IT staff or automated deployment tools such as VSTS. This post provides the steps and code samples for using a JSON configuration file, which can be customized for multiple environments.
Define TypeScript interface for config settings
The use of interfaces in an Angular app provides intellisense and type-safety for your entities. For this example, refer to this sample configuration file.
app-config.model.ts
export interface IAppConfig {
env: {
name: string;
};
appInsights: {
instrumentationKey: string;};
logging: {
console: boolean;
appInsights: boolean;
};
aad: {
requireAuth: boolean;
tenant: string;
clientId: string;};
apiServer: {
metadata: string;
rules: string;
};
}
Create JSON config files
A convenient place to store configuration files is under the assets folder of your project. Using the interface defined above, sample files could look as follows:
assetsconfigconfig.dev.json
{
"env": {
"name": "DEV"
},
"appInsights": {
"instrumentationKey": "<dev-guid-here>"
},
"logging": {
"console": true,
"appInsights": false
},
"aad": {
"requireAuth": true,
"tenant": "<dev-guid-here>",
"clientId": "<dev-guid-here>"
},
"apiServer": {
"metadata": "https://metadata.demo.com/api/v1.0/",
"rules": "https://rules.demo.com/api/v1.0/"
}
}
assetsconfigconfig.deploy.json (Note placeholders that are replaced during deployment)
{
"env": {
"name": "#{envName}"
},
"appInsights": {
"instrumentationKey": "#{appInsightsKey}"
},
"logging": {
"console": true,
"appInsights": true
},
"aad": {
"requireAuth": true,
"tenant": "#{aadTenant}",
"clientId": "#{aadClientId}"
},
"apiServer": {
"metadata": "https://#{apiServerPrefix}.demo.com/api/v1.0/",
"rule": "https://#{apiServerPrefix}.demo.com/api/v1.0/",
}
}
Continue to use environment.ts with Angular-CLI build
The Angular-CLI creates several TypeScript environment files in the environments folder. They will still be used, but contain only the environment name.
environmentsenvironment.dev.json
export const environment = {
name: 'dev'
};
environmentsenvironment.deploy.json
export const environment = {
name: 'deploy'
};
.angular-cli.json
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"deploy": "environments/environment.deploy.ts"
}
Create a service to read config file
If your config file must be read prior to the bootstrapping of your app, you cannot use any Angular services and must rely on pure JavaScript. If this is not the case, you can simply create an Angular service. I will illustrate the more complex scenario.
app.config.ts (Note the use of the interface defined above and config file naming convention to retrieve the appropriate file.)
import { Injectable } from '@angular/core';
import { IAppConfig } from './models/app-config.model';
import { environment } from '../environments/environment';@Injectable()
export class AppConfig {static settings: IAppConfig;
static load() {
const jsonFile = `assets/config/config.${environment.name}.json`;
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.overrideMimeType('application/json');
xhr.open('GET', jsonFile, true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
this.settings = JSON.parse(xhr.responseText);
resolve();
} else {
reject(`Could not load file '${jsonFile}': ${xhr.status}`);
}
}
};
xhr.send(null);
});
}
}
Load config file prior to app creation
main.ts
import { AppConfig } from './app/app.config';if (environment.name !== 'dev') {
enableProdMode();
}AppConfig.load()
.then(() => {
platformBrowserDynamic().bootstrapModule(AppModule);
});
Consume the app settings throughout the application
The config settings are now available from anywhere in the application and they include type-checking provided by the interface.
export class DataService {
protected apiServer = AppConfig.settings.apiServer;
. . .
if (AppConfig.settings.aad.requireAuth) { . . . }
}
export class LoggingService {
. . .
instrumentationKey: AppConfig.settings && AppConfig.settings.appInsights ?
AppConfig.settings.appInsights.instrumentationKey : ''
. . .
if (AppConfig.settings && AppConfig.settings.logging) { . . . }
}
If your Karma and/or Protractor tests require these config settings, a similar change can be added as follows.
test.ts
AppConfig.load().then(() => {
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
. . .
__karma__.start();
});
Note: to build a production version of the app using an environment name other than prod, use this command:
ng build --target=production --environment=deploy