(You can find the code shown here in https://github.com/livioribeiro/angular-dynamic-settings-example)
When developing an Angular application, usually your application settings lives in src/environments/environment.ts
for development and src/environments/environment.prod.ts
for production, and Angular takes care of swapping those during a production build.
But if we have another environment, a staging environment, then we have a problem, since we expect to approve the application running in staging and promote the same code to production, but with Angular's approach to configuration we need to run another build to configure our app to production.
To overcome this problem, I came up a very simple, yet very effective, strategy:
- Load a javascript file before the application starts that will define a settings object in
window.$environment
. This is essentially the same asenvironment.ts
. - In
environment.ts
, export the object defined inwindow.$environment
. - Tell Angular to add the configuration directory to the build output directory.
First we need to create a directory called src/config
and put the javascript file environment.js
there:
// src/config/environment.js
window.$environment = {
production: false,
api: "dev.my-backend.io",
// and any other configuration that would go in "environment.ts"
};
And then load the script on index.html
:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<!-- add the following line -->
<script src="/config/environment.js"></script>
</head>
<body>
<app-root></app-root>
</body>
</html>
Now, in environment.ts
, export the configuration object:
// src/environments/environment.ts
// this interface is just to making things more typed
interface Environment {
production: boolean;
api: string;
}
export const environment = (window as any).$environment as Environment;
And finally, change angular.json
build options, adding "src/config"
the the assets, and remove the "fileReplacements"
completely. I also changed "outputPath"
to just "dist"
:
...
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
// "outputPath" is just "dist"
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets",
// add the following
"src/config"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
// "fileReplacements" is removed
"outputHashing": "all"
},
...
We can safely remove src/environments/environment.prod.ts
, we don't need it anymore.
Now we can inject a configuration script instead of doing another build.
This approach works great with docker and kubernetes, and we can test it right now!
First, we need a Dockerfile:
FROM node:latest as builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:latest
COPY --from=builder /app/dist/ /usr/share/nginx/html/
And a configuration to inject (I called it "environment.js"):
// environment.js
window.$environment = {
production: true,
api: "prod.my-backend.io",
};
We now build the image and run the container with our new configuration:
docker build -t my-app .
docker run --name my-app \
-it --rm \
-p 8080:8080 \
-v $PWD/environment.js:/usr/share/nginx/html/config/environment.js \
my-app
With Kubernetes you can use a configmap to store the "environment.js" and mount it as a volume under "/usr/share/nginx/html/config".
And that's it! No more rebuilding angular for staging and production!
Top comments (0)