Manual bootstrapping angular application : resolve dependencies before angular run block

Manual bootstrapping angular application : resolve dependencies before angular run block

Angular’s run block does not support promises or callbacks which means it can not be made asynchronous. Hence, you can not expect to resolve any asynchronous dependencies in run block before angular bootstraps.

In previous post on splash screen, we have seen exactly where angular bootstrap complete event can be caught, which is in the link function of a directive. Angular’s run block is called when angular creates injector to kick-start your application or when angular bootstrap is complete.

Today, we will be discussing how to manually bootstrap your application.

When you load angular.js script, angular registers all modules and their services, constants, directives and other things. It is just waiting to implement those things somewhere on the DOM. When you add ng-app=”myApp” directive on any HTML tag, angular looks for that on DOMContentLoaded event, and when it finds it, it starts bootstrapping process. It looks like below…

First injector is created for the module, then all templates are complied creating dynamic DOM structure.

But when ng-app=”myApp” is not present, angular have all modules in his memory but no idea where to implement it. That’s where angular.bootstrap functions comes into play. angular.bootstrap function starts your app manually returning injector for that app.

angular.bootstrap(DOMElement, [‘myApp’]) accept first argument which must be a DOM element on which angular will be instantiated. It will have $rootScope of the module myApp. Second parameter will be array of modules to be used for kick-start (but better use only one and using module dependencies, to avoid confusion). angular.bootstrap must be called after DOM is ready. I personally use window.onload callback because I want everything to be downloaded before I remove splash screen.

window.onload = function(){
angular.bootstrap(document.body, ['myApp']);
}

Above will manually bootstrap myApp app on document’s body as parent element.

Now what about resolving dependencies before manually bootstrapping angular app??

Now you know that you can call angular.bootstrap anywhere you want, before which angular will not touch your DOM elements. You can also call this inside then of a promise or callback of any async function.

You might feel little lost because many times you need to make AJAX request to get some data before starting your application, perhaps some authentication, and you love angular’s $http to do that but since angular’s $http is not available in global scope and also if you are not using jQuery then your only solution is to depend on javaScriptsXMLHttpRequest object.

This is not true at all. Even though angular’s services like $http, $animate, $timeout is not available in global namespace but can be extracted from angular.injector function. These services are registered under ng modules.angular.injector([‘ng’]) will return object with extracted service from ng module. Using get function with service name on this object will return injectable service.

var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');

Now you are free to use $http service as you want.

In this example, we will first make AJAX request to API server and get signin status of user. But as we need to store state of the user, we will used predefined module config to store a constant. When we have response back from AJAX request, we will store the response, and bootstrap angular application.

At the same time after calling bootstrap, we will animate using $animate service of angular which will return a promise which will be resolved once animation is completed.

<script>
angular.module('config', []);
angular.module('myApp', ['config']);
    window.onload = function(){
// get injector object
var initInjector = angular.injector(['ng']);

        // extract necessary angular services
var $http = initInjector.get('$http');
var $timeout = initInjector.get('$timeout');
var $animate = initInjector.get('$animate');
        // do operations before bootstrap
// get user sign in status
$http({
url : 'https://api.mysite.com/auth/user/basic',
method : 'GET',
headers : {
'x-auth-token' : localStorage.getItem('auth-token')
}
})
        // when first request resolves
// store user sign in status and other info
// as a constant in `config` module
.then(
// signed in {statusCode : 200} // OK
function(res){
angular.module('config').constant('__user', {
$state : 'signed',
$accType : res.data.accountType,
$accData : res.data
});
},
            // not signed in {statusCode : 403} // Forbidden
function(){
angular.module('config').constant('__user', {
$state : 'unsigned'
});
}
)
        // resolves on either success or failed response
// of previous authentication request
.then(function(){
// start bootstrapping
angular.bootstrap(document, ['myApp']);
            // add `_splash_fade_out` class to splash screen
// when resolved after animation complete, remove element from DOM
$animate
.addClass(angular.element('splash-screen'), '_splash_fade_out')
.then(function(){
angular.element('splash-screen').remove();
});
});
};
</script>

Place above script before the end of body tag.