TL;DR: This article outlines how to manually bootstrap Cordova/AngularJS applications after the deviceready event to prevent Cordova plugin race condition errors.

AngularJS is an extremely popular Single Page Application Framework maintained by Google. Given it’s popularity, it’s no surprise that developers often choose to use AngularJS as their Framework of choice when building a Hybrid Application using Cordova or Phonegap. To get the most out of this pairing, careful steps should be taken when using Cordova and AngularJS together, particularly when dealing with Cordova plugins.

Cordova Plugins are specially prepared code bundles that expose a JavaScript API, allowing HTML5 based Cordova Applications to interact with native device hardware, such as the GPS and accelerometer. Behind the scenes each plugin has native code written in the languages of the supported platforms (ex: Objective-C/Swift, Java, etc), allowing the WebView to retrieve data from device hardware when the JavaScript API is invoked. While the Cordova plugin system is extremely extensible, one common failling is that application JavaScript code may execute before Cordova has finished loading in its entirety. This can lead to plugins not functioning properly, or the application crashing altogether. To avoid this potential race condition, we should leverage the deviceready event provided by Cordova within our applications.

There are several ways we can go about using Cordova Plugins with AngularJS:

  • Do Nothing - If our application isn’t invoking Cordova Plugins during startup, we might avoid this race condition altogether. However, this approach is sloppy, and errors may manifest later as the application grows.
  • Safety check each call - Alternatively, we can wrap each Cordova Plugin call using a wrapper that ensures the deviceready event has been fired. We could configure a Factory to listen for the deviceready event and then chain promises, as seen below. While this works, it still increases the overall complexity of our application by requiring each of the developers to remember to safety check each plugin call.
DeviceReadyFactory.ready()
  .then(function() {
    // Cordova Plugin Call
  });
}
  • Manually Bootstrap Application - Lastly, we can opt to launch our AngularJS application after the deviceready event. With this approach, by the time our AngularJS Application code is running all of our Cordova plugin APIs are safe to execute. Manual bootstrapping allows us to maintain cleaner code at the potential cost of delaying the startup of our application.

In this article we’ll demonstrate one approach to manually bootstrapping a Cordova/AngularJS application. To do this, we’ll discuss automatic and manual bootstrapping of AngularJS applications and show an example Cordova application using AngularJS, ngCordova and cordova-plugin-geolocation.

Bootstrapping

The most common way to initialize an AngularJS application is by using the ng-app directive:

<body ng-app="myApp">
    <!-- AngularJS Code Evaluated here -->
</body>

The ng-app directive performs automatic bootstrapping of an AngularJS application and initializes the root module of the application if one is specified (in this case, myApp). The ng-app directive also indicates the root element of the AngularJS application - any AngularJS template code or directives found within the root element will be evaluated by AngularJS during the application bootstrapping process.

In order to start our AngularJS application after the deviceready event, we will need to have manual control over when our AngularJS application bootstraps. To accomplish this, we can instead utilize the (https://docs.angularjs.org/api/ng/function/angular.bootstrap)[bootstrap] function provided by angular. The bootstrap function allows us to specify the root element of our application, as well as any AngularJS modules that should be initialized. For example, we could instead launch the above application with the following JavaScript code:

angular.bootstrap(document.body, ['myApp']);

Note: Only use one bootstrap method for an AngularJS application. If using angular.bootstrap, do not include an ng-app directive and vice versa.

Using the AngularJS Bootstrap function, we can now initialize our AngularJS application in the deviceready event listener callback function:

document.addEventListener('deviceready', function() {
    angular.bootstrap(document.body, ['myApp']);
}, false);

You can see this approach demonstrated in the sample AngularJS/Cordova application detailed in the subsequent sections.

Application Architecture

Our sample Cordova/AngularJS application consists of one view that displays Geolocation Data in a Bootstrap Panel.

The application itself consists of three main files:

index.html - Contains a single AngularJS view using ng-controller to provide context data. app.js - Declares our AngularJS Module and defines the ApplicationController. init.js - Contains JavaScript code responsible for bootstrapping our application using an extension of the manual bootstrapping technique. In addition to bootstrapping after the deviceready event, the application will launch if run outside of the Cordova environment, which may be useful for live reload tools or debugging.

Dependencies

Our example uses the following dependencies for this example, managed via bower:

  • Cordova (5.4.1)
  • AngularJS (1.5.5)
  • ngCordova (0.1.26-alpha)
  • Bootstrap (3.3.6)
  • jQuery (2.2.3)

We also be used the cordova-plugin-geolocation and cordova-plugin-dialogs plugins in conjunction with ngCordova.

Setup

For this example we created a default Cordova application with the CLI and installed the above dependencies using Bower. We then installed the specified Cordova plugins using the CLI.

You can check out the complete code on github.

Conclusion

Manually bootstrapping your AngularJS application after the deviceready event is just one method of avoiding potential race conditions when using Cordova plugins in AngularJS.

Do you have different ways of handling Cordova Plugins and AngularJS? Post them below!