This article was originally published in 2018, but has been updated to bring it into line with Angular 18.
A full example project to accompany this post can be found here.
Angular applications often need settings which vary depending on which environment they are running in. For example, when running in production, an application may need to use a different URL to connect to a backend api, than it does when running in a staging environment, or locally on a developer’s machine.
One method often used is to put the settings in environment.ts file, and include an additional environment.{env-name}.ts files for each environment we need. With these files however, we need to specify the environment when we build our application, and the appropriate file is included in compilation - baked in, as it were. This isn’t great when we want deploy a single built image to multiple environments.
As a general principle, it’s best to deploy a single built artifact, regardless of which environment we’re deploying to, supplying settings when a container is started up. For that we need a different approach.
First, we put the settings we need for local development into a json file. This file is put into the public folder of our Angular application (in previous versions this would have gone into the assets folder). Files in this folder are copied unchanged to the dist folder when the application is built. In this example I’m calling the file settings.json, and it contains just a single setting - the URL of the api we want to connect to:
|
|
Next we create a class, settings.ts to represent the settings in our Angular application:
|
|
Then an Angular service which can be injected into any class which needs access to the settings:
|
|
and another Angular service which will fetch the settings when the Angular app starts up in the browser:
|
|
This last service needs to be registered as an APP_INITIALIZER in app.config.ts, which ensures that it’s run when the application starts up, and before any user interactions occur.
|
|
With this in place, the application should load the settings provided in the settings.json file on startup. Any component which needs the settings can inject the SettingsService and access the settings from there.
To vary the settings based on environment, I add an additional file in the assets folder:
|
|
This has the same structure as the settings.json file, but specifies the name of an environment variable, instead of the setting value.
Then I modify my Dockerfile, so that when the container starts up, it replaces the environment variable names in the template file, with the values of those variables, and overwrites the settings.json file with the result, before starting the webserver.
|
|
In this case I’m using the standard nginx image — if you’re using something different you’ll need to make the appropriate changes.
Here in the CMD statement, I’m using the standard Linux envsubst command to substitute the environment variables in settings.template.json and write the result to settings.json, and then I startup nginx.
The image can be built and the container started, as follows:
|
|
Then you can open a browser and navigate to http://localhost:8080 to see the application running. The URL provided in the API_URL environment variable will be displayed in the application.
So now a single image can be deployed to any number of environments, and the settings varied accordingly.
A full example project to accompany this post can be found here.