Managing settings for an Angular app with VSTS

Recently I needed to have client side settings in an Angular app. My app had to call some 3rd party webservices and their location was different per environment (test, QA and production), I also had to configure a couple of parameters that were necessary when calling them.

There’s support for this in Angular using “environments”. If you scaffold a new Angular app with angular-cli you’ll get a folder “Environments”.

environments

These files can be used while building the application. Specifying the environment will bundle the environment specific file together with the rest of your application. Which is great but I don’t want to rebuild the application when I go from test to QA and finally to production. Typically settings are changed when deploying to an environment, at least that’s what I usually do with my .NET projects.

Trying to have one unified way of handling the settings gives two challenges: how to bootstrap the Angular app with dynamic settings and how to manage these during deployment.

I added a client-config.json file in my asset folder to contain all the settings and the values which were correct during development.

client-config.json

This file needs to be downloaded before most of the application actually starts so I added a settings provider.

import { Http } from '@angular/http';
import { Injectable } from '@angular/core';
 
@Injectable()
export class SettingsProvider {
 
  private config : any;
 
  constructor(private http: Http) {
  }
 
  public loadConfig() : Promise<any>{
    return this.http.get("assets/client-config.json")
      .map(res => res.json())
      .toPromise()
      .then(settings => this.config = settings);
  }
 
  public get configuration(): any {
    return this.config;
  }
}

Then in my app.module.ts I add a new function to load the configuration.

export function init(settingsProvider: SettingsProvider) {
  return () => settingsProvider.loadConfig();
}

I also changed the NgModule declaration to include

  providers: [{
    'provide': APP_INITIALIZER,
    'useFactory': init,
    'deps': [SettingsProvider],
    'multi': true
  },
  SettingsProvider]

The crux here is the APP_INITIALIZER token which Angular provides to run logic before the application starts. You can read more about it here and here.

With the client side now done, the application still needs to get the correct values when the application is pushed to different environments. This is the easy part as VSTS has support for file transforms and variable substitution. I just add my client-config.json to the list of files that need to be changed. VSTS will replace any variable it finds that can be matched with variables defined for the environment where I deploy.

VSTS Config

The client-config.json is also added to the list of files the Angular build needs to pick up.

 "apps": [
    {
      "root": "src",
      "outDir": "../wwwroot",
      "assets": [
        "assets",
        "favicon.ico",
        "client-config.json"
      ],
      ....
    }]

The end result is a single way to manage environment specific settings for my .NET Core and Angular applications.

13 Replies to “Managing settings for an Angular app with VSTS”

  1. thanks for describing this. angular environments are really not a good fit for vsts build/release flows (one build).

    however hat to add the following to be able to get the file from the azure app service:

  2. Great guide Benny! I was searching high and low on how to solve this using a Release step without having to build each configuration.

    On a side note, I found my site wouldn’t load and returned a 404 on the client-config.json file until I added a web.config file to serve out staticContent. Is this something that you had to do yourself or did you solve this through another means?

    1. Hi Wah

      Glad it could help you. I did not run into the problem you mention as I use the staticfiles middleware to serve files. Should have added that to the article 🙂

    2. hi, i have the same issue of 404 on the client-config.json. What needs to be added in web.config to serve staticContent. I am using angular6

  3. Thanks for your detailed post, it works like a charme 🙂
    However, I need to inject some of those config values into my environment.ts – but I don’t have dependency injection there (since it’s just a constant).

    My desired solution would be something like this:

    export const environment = {
    production: true,
    apiBaseUrl: ‘/’,
    clientId: settingsProvider.configuration.clientId, // <– Here's the problem, because I cannot inject settingsProvider
    enableAdalDebug: false
    };

    Do you know any solution for that?

    1. Hi Dave

      Aren’t you back at the original problem with that approach? You will have to rebuild for each specific environment. You could create a wrapper around environment which combines the json approach illustrated in this post and the environment object.

  4. Hi Benny,

    thanks for your quick answer.
    On the one hand you’re right, that goes back to the original problem.
    However, in my use case I have two different scenarios:
    I’m using the environments in order to differentiate between a local (=dev) and a deployed version. I need that in order to have another backend URL (because backend is on another port locally).
    Additionally, I need to import the VSTS settings, which then goes back to your approach. But after some research and a stack overflow (https://stackoverflow.com/questions/48559287/inject-variable-from-service-in-environment-ts) I’ve come to the conclusion that there’s no way out than having two config files :/

    Kind regards,
    Dave

    1. It’s just a json file. It can have the same structure as appsettings but might be entirely different depending on your need. You might not need everything. Be carefull with what you put in because it will be available clientside.

    1. In my case it looked like this:
      {
      “tenant”: “”,
      “clientId”: “”,
      “domain”: “”,
      “ApplicationInsights”: {
      “Config”: {
      “instrumentationKey”: “”
      }
      }
      }

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.