How To Bootstrap Angular with Server Side Data


Today I needed to bootstrap our Angular 1.x Single Page Application (SPA) with some server side data. The data that I’m talking of is the set of feature toggles that are defined in our system. We need the value of those feature toggles to configure some of our Angular providers and services. This is the solution I came up with. Note, it has been heavily inspired by a sample found here.

The code to this post can be found here

If you like to read more posts of me where I talk about Angular please refer to this index

The Solution

The main idea is to not let Angular bootstrap itself auto-magically using the ng-app directive but to rather bootstrap the framework explicitly. This looks similar to this. First we define an element in our main page (e.g. index.html) with the id of the application, e.g. app. I would use the element that usually would contain the ng-app directive. Thus it would look along the line of this

<div id='app'>
   ...
</div>

And now we can add code like this to our main JavaScript file, e.g. app.js

angular.element(document).ready(function () {
  var $injector = angular.injector(['ng','Flags']);
  var FeatureFlags = $injector.get('FeatureFlags');
  FeatureFlags.init().then(function () {
    var el = document.getElementById('app');
    angular.bootstrap(el, ['app']);
  });
});

That is, as soon as the browser has loaded the DOM and is ready we execute the code above. First we get an Angular injector which contains all the modules that we need to be able to create the service that will load our server side data. In our case the service is called FeatureFlags and is implemented in an Angular module called Flags. We then use the $injector to retrieve/construct an instance of our service. Then we call the init function of the service which asynchronously loads the server data. Since the init method is asynchronous it returns a promise. We now define the success function of the promise which gets called when the data successfully has been loaded from the server. Inside this success function we identify the element with id=='app' in the DOM and use it to bootstrap angular. Note that we also declare in the bootstrap function that our main Angular module is app.

It is important to notice that the $injector that we create in the snippet above is a different instance of the Angular injector than Angular itself will use once it is bootstrapped!

The FeatureFlags Service

In a new file, let’s call it flags.js we add the following code

angular.module('Flags',[])
    .factory('FeatureFlags', FeatureFlagsService);

This creates a new Angular module Flags with no external dependencies. We then add a factory called FeatureFlags to the module. The implementation of this factory is represented by the function FeatureFlagsService.

Now let’s turn to the implementation of the service. In this example we are simulating the server backend by using the $timeout service of Angular and not the $http service that we would typically use to make remote and asynchronous server calls. The $timeout service helps us to make everything asynchronous. Here is the skeleton of the service

function FeatureFlagsService($q, $timeout) {
    var service = {
        timeoutService: $timeout,
        qService: $q
    };
    service.init = function(){
        return this.loadFeatureFlags();
    };
    service.getFeatureFlags = function(){
        return this.features = this.features || window.__otxp_featureFlags__;
    };
    service.getFeatureFlag = getFeatureFlag;
    service.loadFeatureFlags = loadFeatureFlags;
    return service;
}

So, the init function uses the loadFeatureFlags function which returns a promise to load the feature flags from the server. Let’s look at the implementation of this beauty

function loadFeatureFlags() {
    var features = [{
        "name": "sso",
        "active": 1
    },
    {
        "name": "abc",
        "active": 0
    }]
    return this.timeoutService(function(){
        // Avoid clash with other global properties 
        // by using a "fancy" name
        window.__otxp_featureFlags__ = features;
    }, 2000);
};

First I’m defining some sample feature toggles (consisting each of a name and active property). Then I use the $timeout service to asynchronously return those features with a delay of 2000 ms and assigning them to a global variable on the window object. I have chosen a “fancy” name to avoid a clash with any other potential global variables.

In the real service we would use the $http service instead of the $timeout service like this

var url = '[url to the server API]';
$http.get(url).then(function(response){
    window.__otxp_featureFlags__ = response.data;
});

Assuming that the server returns the feature flags as a JSON formatted object in the response body.

Finally the implementation of the getFeatureFlag function looks like this

function getFeatureFlag(feature) {
    var result = this.getFeatureFlags().filter(function(x){ return x.name == feature; })[0];
    var featureIsOn = (result === undefined) ? false : result.active != 0;
    return featureIsOn;
}

With this we have completely defined our service that is used to asynchronously load the server side data and make it available to us in the Angular application.

The App Module

Now it is time to define the main Angular module. We called it app. Here is the skeleton of it. I have added this code to the file app.js where we also have the Angular bootstrap code

angular.module('app', ['Flags'])
    .run(function ($rootScope, FeatureFlags) {
        $rootScope.features = FeatureFlags.getFeatureFlags();
        $rootScope.done = FeatureFlags.getFeatureFlags() ? 'Booted!' : 'Failed';
    })
    .provider('Auth', AuthProvider)
    .directive('ngLoading', LoadingDirective)    
    .controller('appCtrl', appCtrl)

Our app module is dependent on the Flags module where we have implemented the FeatureFlags service. In the run function of the module we use this service to retrieve the feature flags and assign them to the features property of the $rootScope.

We also add a provider Auth, a directive ngLoad and a controller appCtrl to the module. As we will see, we will need the feature flags in the definition of the Auth provider. Thus let’s start with the implementation of that provider

The Auth Provider

The implementation of the Auth provider, as said above, depends of a feature flag. We have a legacy implementation if the feature flag is OFF and a new implementation if the flag is ON. I have organized the code for this in a file called auth.js.

function AuthProvider(){
    return ({
        $get: function(FeatureFlags){
            var service;
            var isOn = FeatureFlags.getFeatureFlag('sso');
            if(isOn){
                service = AuthFunc();
            } else{
                service = LegacyAuthFunc();
            }
            return service;
        }
    });
} 

The provider implements the $get function and in it uses the FeatureFlags service to evaluate whether or not the Single Sign On (sso) feature is enabled or not. Depending on the setting the provider returns a different implementation of the authentication service. In this simple demo app those implementations look like this

function AuthFunc(){
    var service = {};
    service.getMessage = function(){
        return "I'm the Auth service";
    }
    return service;
}

function LegacyAuthFunc(){
    var service = {};
    service.getMessage = function(){
        return "I'm the *legacy* Auth service";
    }
    return service;
}

Finally we come to the reason of all this effort. We want to inject the authentication provider into the controller appCtrl and of course expect to get the correct implementation there. Here is the code for my sample controller

function appCtrl($scope, Auth){
    $scope.message = 'Hello: ' + Auth.getMessage();
}

And as we can see when running the application in a browser we get the expected message back from the Auth service depending on the setting of the sso feature flag. The full sample can be found here

Summary

In this post I have shown you how we can use custom bootstrapping for Angular to allow us to use server side data during the bootstrap of the application. I have tried many other options but this seems to be the only reliable and reasonable way I have come up with. Hope this helps. Stay tuned.

If you like to read more posts of me where I talk about Angular please refer to this index

Docker and Swarmkit – Part 4