Asynchronous JavaScript with async/await

In this course we will learn how to use the ES2017 async and await keywords to write asynchronous code that is more readable and easier to follow than equivalent code based on long promise chains or deeply nested callbacks.

Watch the Course

Asynchronously Bootstrapping AngularJS Applications with Server-Side Data

I recently worked on an AngularJS application that needs to be bootstrapped with some data from our server backend before it's being started. Pretty much all components of the application depend upon that server-side data, hence the data has to be accessible by the time the components are initialized.

If you want to bootstrap an AngularJS application with data from your backend, e.g. some essential configuration data, you basically have two main options. You can either …

  • embed the data in the HTML document, or
  • fetch it by making an additional HTTP request.

I've already written about how to inline .NET server-side data into the HTML response in Bootstrapping AngularJS Applications with Server-Side Data from ASP.NET MVC & Razor. This post is going to be about making an additional AJAX request to asynchronously fetch some server-side JSON data from a dedicated HTTP endpoint.

Automatically Bootstrapping an AngularJS Application

To initialize an AngularJS application, you would usually place the ng-app attribute on an HTML element which defines the application's scope. As soon as the DOM content has finished loading, Angular will take care of the setup process itself and will bootstrap the application:

<html ng-app="myApplication">
    <!-- ... -->
</html>

Unfortunately, this doesn't work with asynchronously loaded data which your application requires right from the start. Angular won't wait for your AJAX request to finish before starting the bootstrap process, so there's a high chance the data won't yet be loaded by the time the application is running.

Because it's very likely that the server-side data hasn't finished loading, your AngularJS controllers, directives, and other components have to be able to deal with missing data. You might find yourself writing guard clauses and checks for undefined all over your code base, if your application is able to work without the data at all. This is terrible!

Luckily, AngularJS applications can also be bootstrapped programmatically.

Manually Bootstrapping an AngularJS Application

Let's start by defining our application's main module:

var myApplication = angular.module("myApplication", []);

Now, instead of relying on the ng-app attribute, we can call the angular.bootstrap function manually. We need to hand it both the application root and the name of our main module. Here's how you call it as soon as the DOM has finished loading:

angular.element(document).ready(function() {
    angular.bootstrap(document, ["myApplication"]);
});

That should already be enough to get the application running. (Make sure to remove the ng-app attribute from your HTML!) We can now defer this initialization process until we've successfully grabbed the required data from the server. That will ensure that we won't have to worry about temporarily missing data.

Fetching the Required Data from the Server

We'll use Angular's $http service to make an AJAX request to the server. To use that service, we first have to get hold of the injector which usually performs dependency injection within the Angular infrastructure:

var initInjector = angular.injector(["ng"]);

Now we can resolve the dependency to $http like this:

var $http = initInjector.get("$http");

Let's now make the AJAX request to fetch the JSON data (configuration details, in my example) and store it in an Angular constant called config which we can access later within all of our controllers, services, and so on:

$http.get("/path/to/data.json").then(function(response) {
    myApplication.constant("config", response.data);
});

Et voilà, here's our required data, readily available to us. This is what our code looks like if we clean it up a little:

(function() {
    var myApplication = angular.module("myApplication", []);

    fetchData().then(bootstrapApplication);

    function fetchData() {
        var initInjector = angular.injector(["ng"]);
        var $http = initInjector.get("$http");

        return $http.get("/path/to/data.json").then(function(response) {
            myApplication.constant("config", response.data);
        }, function(errorResponse) {
            // Handle error case
        });
    }

    function bootstrapApplication() {
        angular.element(document).ready(function() {
            angular.bootstrap(document, ["myApplication"]);
        });
    }
}());

Note that we're returning a promise from the fetchData function so that we can chain the call to bootstrapApplication using then.

Warning, Here Be Dragons!

While the described approach works nicely, it doesn't come without some disadvantages. Think about how the browser loads the AngularJS application:

  1. A request to the initial HTML document is made (request #1).
  2. The document is returned. It references some JavaScript files.
  3. The referenced script files are loaded (request #2).
  4. Execution of the returned JavaScript files begins.
  5. Our script kicks off the AJAX request (request #3).
  6. The AJAX request returns with the required data.
  7. Finally, our AngularJS application is bootstrapped.

Notice that we're making three sequential HTTP requests until we can bootstrap our application. Depending on latency and bandwidth, that might result in a noticeable delay when loading the page.

Also, the boostrapping of the AngularJS application entirely depends on the AJAX request being successful. If the request fails, the application won't be initialized at all. You should consider this and implement a retry mechanism or provide some default data in case of a loading error.

Hope this helps, happy coding!

Related Posts

More AngularJS Material:

Learn Node