Angular 2+ - Dynamically change base path of app
It took some time but we finally managed to find an easy enough solution to cover all the points we made in the question.
The solution is quite close to the original approach of just building the Angular app offline and copying the content inside an nginx
container, mixed with the build process of previously used in the init container.
Our solution then looks as follows. We first build the application offline and inject some recognisable string where the base-href
and the deploy-url
should be later.
ng build --prod --base-href=http://recognisable-host-name/ --deploy-url=http://recognisable-host-name/
The resulting runtime*.js
and the index.html
file will contain these inside the code and as the base for assets to load.
Then in the second phase the Dockerfile for a standard Angular application is adapted from
FROM nginx:1.17.10-alpineEXPOSE 80COPY conf/nginx.conf /etc/nginx/conf.d/default.confRUN rm -rf /usr/share/nginx/html/*COPY dist/ /usr/share/nginx/html/
to the new version like this
FROM nginx:1.17.10-alpineEXPOSE 80COPY conf/nginx.conf /etc/nginx/conf.d/default.confRUN rm -rf /usr/share/nginx/html/*COPY dist/ /usr/share/nginx/html/ENV CONTEXT_PATH http://localhost:80ENV ENDPOINT http://localhost/endpointENV VARIABLE foobarCMD \ mainFiles=$(ls /usr/share/nginx/html/main*.js) \ && \ for f in ${mainFiles}; do \ envsubst '${ENDPOINT},${VARIABLE}' < "$f" > "${f}.tmp" && mv "${f}.tmp" "$f"; \ done \ && \ runtimeFiles=$(grep -lr "recognisable-host-name" /usr/share/nginx/html) \ && \ for f in ${runtimeFiles}; do sed -i -E "s@http://recognisable-host-name/?@${CONTEXT_PATH}@g" "$f"; done \ && \ nginx -g 'daemon off;'
Here first we pass the substitutions we would like to make. First the CONTEXT_PATH
is the full base path that will replace the http://recognisable-host-name
string we injected before, by first finding all the files containing the string and then replacing the it with the sed
command.
The other variables that may be environment specific can also be replaced by utilising envsubst
and replacing all mentions of those variables from the main*.js
files. Therefore the environments.prod.ts
is prepared as follows:
export const environment = { "endpoint": "${ENDPOINT}", "variable": "${VARIABLE}"}
This takes care of all the main points I raced, namely:
- do not share the original code
- make a deployment fast
- container can be served behind any proxy (e.g. Nginx-Ingress/Kubernetes)
- environment variables can be injected
I hope it helps somebody else, especially because as I was researching this topic I found dozens of articles but none met all the criteria or necessitated lots of custom code that is complex or inefficient or hard to maintain.
Edit:
If somebody is interested, I set up a demo project where this solution can be tested:
https://gitlab.com/satanik-angular/base-path-problem