Angular: Using Config File After Build

Background: I've got an Angular app being built by TeamCity and deployed by Octopus Deploy to multiple environments. For each environment, I also have a different API Url that it will talk to.

The whole source code covered in this post is available here

Using the default Angular environment variables is not ideal in this case, because I don't really want to build the application multiple times, I want to build it once and let Octopus change some configuration files depending on the environment.

My idea was simple: add a config.json file to the assets folder, and then inject this file some how in the module and use it.

Not that simple after all, but I got there. Let's see how I did it.

  1. I did add a config.json file to the assets folder and when building the app with ng build the file is automatically added to dist\assets folder.

  2. I couldn't find any simple way to just get a config.json from the assets folder and inject into a module. So I ended up having to create a service for that which would use the default javascript XMLHttpRequest to do the job.

@Injectable()
export class ConfigService {

    public config: any;
    constructor(private http: Http) {
    }

    loadJSON(filePath) {
        const json = this.loadTextFileAjaxSync(filePath, "application/json");
        return JSON.parse(json);
    }

    loadTextFileAjaxSync(filePath, mimeType) {
        const xmlhttp = new XMLHttpRequest();
        xmlhttp.open("GET", filePath, false);
        if (mimeType != null) {
            if (xmlhttp.overrideMimeType) {
                xmlhttp.overrideMimeType(mimeType);
            }
        }
        xmlhttp.send();
        if (xmlhttp.status == 200) {
            return xmlhttp.responseText;
        }
        else {
            return null;
        }
    }
}
  1. That actually was not enough to inject it into module, because what I had before was { provide: API_BASE_URL, useValue: 'http://localhost:4444' }, as I'm using a generated service created by NSWAG. So I had to find a different way to achieve that. I had to create a ConfigFactory to use in the provider like useFactory: ConfigFactory. That's how the ConfigFactory looks like:
export function ConfigFactory(configService: ConfigService, file: string, property: string) {
    return configService.loadJSON(file)[property];
}
  1. Now I just need to use the ConfigFactory. I added 3 extra entries to my list of providers: CONFIGPATH is path to the config.json file, APIURLVAR is the variable I'm going to extract from the config.json and finally the API_BASE_URL is the one using the ConfigFactory.
{ provide: 'CONFIGPATH', useValue: '/assets/config.json' },
{ provide: 'APIURLVAR', useValue: 'API_BASE_URL' },
{
  provide: API_BASE_URL, useFactory: ConfigFactory,
  deps: [ConfigService, 'CONFIGPATH', 'APIURLVAR']
}]

Now you can build your app once and modify the API Url based on the environment you're going to deploy to, which is easily configured in Octopus Deploy.

Hope it helps.

Cheers,
Thiago.