README.md

Dockerizing an Angular app

Dockerizing an Angular app is quite easy. I will show you how to do that in this
document's first part.

It gets a litte bit more complicated to follow Docker's "Build once, run
anywhere" motto. The document's second part will show you a possible solution to
this challenge.

I won't explain any Docker concepts here. I'm sure you'll easily be able to find
a documentation or tutorial fitting your needs.

Part I: The Simple Case

To run an Angular app inside a Docker container without further configuration
from the outside, you don't have to change any code in your app. All you've got
to do is to add some files and edit them to match your app's name and the target
environment's port the app should listen to.

Here you
can find the sources that are included in this part of the document.

If you're like me, you love automating steps you need to repeat often (and which
you will most likely forget if another project draws your attention). Because of
this, I'm using the following two scripts:

A dockerize.sh script to automate building the image (use npm instead of
yarn, if necessary):

Visit http://localhost:8093 (that's the port defined in the
docker-compose.yml file)

Part II: Build Once, Run Anywhere

Here you can find the sources
that are included in this part of the document.

Think of a typical development process that requires the following environments:
Development, testing, staging, and production. In each of these, you will
probably at least need a different base URL pointing to the location where the
backend resides.

In the first environment, the app needs to run wth ng serve and
ng serve --prod from your shell. So you need some ability to inject the "base
URL" as I'm going to call it into your app without any Docker container running.

In the other environments, Docker needs to overwrite the base URL you need for
development with the one fitting into to the individual environment consisting
of many containers being carefully linked together. You need to somehow inject
the base URL here, too.

One thing should be clear: You don't want to build a new image containing your
Angular app for each of these environments. The tested image is the one you want
to deploy in staging and production. Why? Because on the build system, things
might have changed between the first and the next build. For example, a new npm
release might have crept in. You might have updated global packages. Someone
might have made a tiny hotfix in your code base. All of these might result in a
slightly different build that is different from the one you had tested
thoroughly.

What you need is a configurable image that works in every environment. We'll
concentrate on the base URL. All other configurations should work the same way.

First of all, we need some mechanism to load the app configuration at runtime.
If we'd use Angular's environment.ts for this purpose, the value would need to
be set at build time. That's too early. So, what we need to do is put the
configuration in some file which we'll place in the assets folder. This way,
we can easily overwrite the file when composing the container, i. e. at
runtime. We'll see how to do this in a moment.

There's one thing we need to cope with when we're retrieving the settings from
the JSON file: The app already needs its settings when it's launching, but the
settings are retrieved via an HTTP call, which is done asynchronously.
Thankfully, Angular introduced the concept of an APP_INITIALIZER for that. I
won't go into detail here. The point is: The app awaits the APP_INITIALIZER's
result before continuing to initialize, and that's exactly what we need here.

So, here's the SettingsInitializerService that is responsible for loading the
Settings: