Customizing Angular App Behavior Per Build Environment
Tags: angular
Published: 2019-07-02 06:00:24

If you have used angular for even a brief period, you probably have come across the environment configuration files. By default, they are two of them – one for production and another for development. They allow you to specify configs or settings that are unique to a specific deployment environment. This may include configs such as the URL API Endpoint or even Firebase Configs for the said environment.

For instance, Angular uses the `prod` property from environment variables to enable the Service Worker when true – i.e. in production deployment only.

```typescript
ServiceWorkerModule.register('ngsw-worker.js', {
  enabled: environment.production
}),
```

You can also build on this, so that you can have even more configs for different target environment. For instance – `development`, `staging` and `production`. To learn more about mastering environment configurations, checkout my previous [post](https://codinglatte.com/posts/angular/environment-variables-angular/) here.

In this post, we are going to take environment specific configurations a bit further. We are going to see how we can customize not only the variables but styles, scripts, assets and even classes. In order to do this, you will need to be comfortable tinkering with the `angular.json` – the config file your angular [workspace](https://angular.io/guide/workspace-config).

### Why

For most project, including most of mine, the default Angular CLI configs work out of the box, with no or minimal need for any customization.

But, from time to time, you may need to customize the behaviour of your angular application based on the environment you are building it for. For instance, you may want to have a different theme, logo etc. for your alpha, beta and production environments – enabling your users/testers to easily identify which environment they are in.

In such a case you could have different colors for your different environments, while also correcting more telemetry information on the pre-release versions as compared to the stable release, where most of users will be.

So, without further ado, let’s get started:

### Customizing Styles and Scripts

By default, if you have some global styles and scripts for your web app, you can add them to the styles array.

```json
{
  ...
  "projects": {
    "angular-extreme-build-configuration": {
      ...
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            ...
            "styles": [
              "styles1.scss",
              "styles2.scss",
              ...
            ],
            "scripts": [
                "script1.json",
                "script2.json",
                ...
            ]
          },
          ...
        },
        ...
      }
    }
  },
  ...
}
```

This will and other configuration you make in this section will act as default configuration for all build environment (i.e. prod, serve, staging) you have setup for your app. But what we want in our case, is to set different styles for different environment we are targeting.

To achieve this, we are going to pass a different set of styles and scripts for each environment. You do this by adding the styles and scripts properties under the environment you want to. Each property accepts an array of file paths relative to the root of the angular workspace.

```json
{
  ...
  "projects": {
    "angular-extreme-build-configuration": {
      ...
      "architect": {
        "build": {
          ...
          "options": {
            ...
            "styles": [
              "src/styles.scss" // default
            ],
            "scripts": []
          },
          "configurations": {
            "staging": {
              ...
              "styles": [
                "src/styles.staging.scss"
              ],
              "scripts": []
            },
            "production": {
              ...
              "styles": [
                "src/styles.prod.scss"
              ],
              "scripts": []
            }
          }
        },
        ...
      }
    }
  },
  ...
}
```

> **NB:** Any environment, that you don’t configure the scripts and styles for, will fall-back to the default. So, you can add the default scripts and styles, then configure the specific environment you want to be different.

For instance, if you were using bootstrap or any other CSS framework, you can theme your web app differently based on the environment. This gives you the freedom to customize the look and feel of your web app across multiple target environments. You can have a production web app that looks completely different from staging, yet the underlying code is largely the same.

Checkout the demo [here](https://github.com/mainawycliffe/angular-extreme-build-configuration), to see this in action.

### Unique Assets for Different Target Environments

Just like Assets and Scripts above, you can also provide a list of assets for your app. Basically, this are files that are not part of your app code. When you specify them, Angular CLI will then copy them as they are, to the build directory during build time. This is suitable for things like logos, icons etc.

By default, you specify individual images or directory and Angular will copy them as they are, relative to the root of angular workspace.

```json
{
  ...
  "projects": {
    "angular-extreme-build-configuration": {
      ...
      "architect": {
        "build": {
          ...
          "options": {
            ...
            "assets": [
                "src/favicon.ico",
                "src/assets"
            ],
            ...
          },
          ...
        },
        ...
      }
    }
  },
  ...
}
```

This will not work very well for multi environment configuration. In such situations, you can provide more instruction to angular cli on how to copy assets for a specific environment. Instead of a simple path to the file or directory, you can specify a field with the following fields:

1. `glob` - This specifies the file pattern to copy, uses the input directory as the base
2. `input` - The input directory, relative to the angular workspace
3. `output` - The output directory, relative to the build directory
4. `ignore` - the list of globs to ignore in the input directory

Take this scenario, you have two build environments, staging and production, each with a unique asset. First, you create two directories, one for production assets – `prod_assets` and the other for staging assets – `staging_assets`. During build for each of this configuration, you need the prod and staging assets copied over to the `assets` directory in the build directory.

In this case, your staging assets configuration would look like this:

```json
{
  ...
  "projects": {
    "angular-extreme-build-configuration": {
      ...
      "architect": {
          ...
          "configurations": {
            "staging": {
              ...
              "assets": [
                "src/favicon.ico",
                {
                  "glob": "**/*",
                  "input": "src/staging_assets/",
                  "output": "/assets/"
                }
              ],
              ...
            },
            ...
          }
        },
        ...
      }
    }
  },
  ...
}
```

And the production configuration would look like this:

```json
{
  ...
  "projects": {
    "angular-extreme-build-configuration": {
      ...
      "architect": {
          ...
          "configurations": {
            ...
            "production": {
              ...
              "assets": [
                "src/favicon.ico",
                {
                  "glob": "**/*",
                  "input": "src/prod_assets/",
                  "output": "/assets/"
                }
              ],
              ...
            }
          }
        },
        ...
      }
    }
  },
  ...
}
```

### Using File Replacement for Typescript Classes

The configs for File replacement allow you to instruct Angular CLI to replace a specific file with another before building your web app. For instance, you could replace a typescript class with another class – provided the properties and methods required are available in both – depending on your build target environment.

In fact, Angular CLI uses this to replace the default `environment.ts` file with `environment.prod.ts` when building with the `--prod` flag. The setting accepts an array of objects, which means you can have multiple file replacement. The object has two properties – replace and with, where the first is the file to replace, and the latter is the path to the replacement file.

```json
"fileReplacements": [
  {
    "replace": "path/for/file/to/replace",
    "with": "path/for/file/to/replace/with"
  }
]
```

Let’s take this scenario, you have two logging classes, during development, you just want to log to console. But during production you want to log to a logging service – remotely. You could create two classes or services – `logging.ts` and `logging.prod.ts` - and having angular CLI replace the default class with the production class when building for production.

To achieve this, under `fileReplacements` for production configuration, you just need to specify which file to replace and the replacement file, as shown below:

```json
...
"production": {
  "fileReplacements": [
    {
      "replace": "src/app/logging.ts",
      "with": "src/app/logging.prod.ts"
    }
  ],
  ...
}
...
```

I know that is a bit of a stretch using file replacement like that, but I didn’t have a better idea and you still get the point.

That’s it from me on this topic for now. If you have any more ideas on build configuration customization, use the comment section below, it will be helpful to me and future readers of this post.

All the source code for this post can be found on [GitHub](https://github.com/mainawycliffe/angular-extreme-build-configuration). If you have any question for me, feel free to use the comment section below or shoot me a message on [Slack](https://forms.gle/PdD4RcAS8kK6TMbF9).