Loading Configuration Data on Startup with AngularJS

Server-side web frameworks have various ways of storing and loading configuration settings - either use .properties, .xml, .yaml and then apply the built-in framework feature. Rails has excellent support for this and allows you to use a combination of environment-specific code files, .yaml configuration files and environment variables. But how do we push these environment variables into our Angular application?

Rendering configuration values directly into the DOM

Our first approach to solving this problem was to render JSON directly into the DOM via a script tag. You can use any of the various HTML templating solutions to render this script, in my example I’m using HAML.

:javascript
  angular.module('myApp').constant('configuration', {
    externalServiceEnabled: #{ENV['EXTERNAL_SERVICE_ENABLED']},
    externalServiceApiKey: '#{ENV['EXTERNAL_SERVICE_API_KEY']}'
  });

I see a couple of problems with this approach:

  • Our Angular application is now completely tied to the server - we can’t load the Angular app without loading the server as well. We will also need to stub out this constant during tests.
  • We have to serve the Angular application through our server framework - we can’t use something like Webpack to serve our JavaScript.
  • We can’t tell the Angular application to reload configuration at run-time - we will need to force users to reload the HTML page in order to get new configuration settings. This also inteferes with our caching strategy - we will have to rethink the caching of our templates and we can’t host our templates on a CDN.

Loading configuration data on startup

A more natural approach is to load the configuration data from the server - your Angular application is already making a multitude of calls to the server in order to fetch application data, why can’t we load configuration data in the same way? Of course, this is slightly different since the configuration data needs to be loaded before Angular starts, but the principal is the same. Let’s take a look at how we can make this happen.

I’m going to show how to migrate from the approach above (where the configuration is injected as a constant) to an approach where the configuration is loaded on startup. Ideally I would like to be able to use the same configuration object as before.

I’m going to start by writing a provider for the configuration object. Providers are probably the least-used entity in Angular applications, but they are also the most flexible - Value, Factory, Service and Constant are actually all just syntactical sugar on top of Provider. If you’re not familiar with providers it’s definitely worth researching, but for now you just need to know that the injector will call the $get function when it needs to inject your object.

angular.module('plunker').provider('configuration', function() {
  let configurationData;

  this.initialize = (data) => {
    configurationData = data;
  };

  this.$get = () => {
    return configurationData;
  };
});

Now we need to manually bootstrap the angular application - so instead of using the ng-app directive we will manually tell Angular to bootstrap our application.

angular.element(document).ready(() => {
  angular.bootstrap(document, ['plunker']);
});

Now for the final step - we need to load the configuration data and use the response to initialize our configuration provider.

angular.element(document).ready(() => {
  $.get('/configuration.json', (response) => {
    angular.module('plunker').config((configurationProvider) => {
      configurationProvider.initialize(response);
    });

    angular.bootstrap(document, ['plunker']);
  });
});

This will ensure that the configuration data is loaded before our Angular application starts - so we can always expect the configuration data to be present at runtime. Note that I’m using jQuery to make the actual AJAX call since you don’t have access to Angular’s $http service at this point.

You can find all the code on Plunker. Happy coding.