Cordova + Angularjs + Device Ready

I'm using the following solution, which allows AngularJS to be bootstrapped when running with Cordova as well as when running directly in a browser, which is where much of my development takes place. You have to remove the ng-app directive from your main index.html page since that's what the manual bootstrapping is replacing.

UPDATE: I've since switched to the following method, which I think is cleaner. It works for Ionic as well as vanilla Cordova/PhoneGap. It should be the last bit of JavaScript to run - perhaps inside a script tag before the /body tag.

  angular.element(document).ready(function () {
    if (window.cordova) {
      console.log("Running in Cordova, will bootstrap AngularJS once 'deviceready' event fires.");
      document.addEventListener('deviceready', function () {
        console.log("Deviceready event has fired, bootstrapping AngularJS.");
        angular.bootstrap(document.body, ['app']);
      }, false);
    } else {
      console.log("Running in browser, bootstrapping AngularJS now.");
      angular.bootstrap(document.body, ['app']);
    }
  });

Here's the older solution I used:

// This is a function that bootstraps AngularJS, which is called from later code
function bootstrapAngular() {
    console.log("Bootstrapping AngularJS");
    // This assumes your app is named "app" and is on the body tag: <body ng-app="app">
    // Change the selector from "body" to whatever you need
    var domElement = document.querySelector('body');
    // Change the application name from "app" if needed
    angular.bootstrap(domElement, ['app']);
}

// This is my preferred Cordova detection method, as it doesn't require updating.
if (document.URL.indexOf( 'http://' ) === -1 
        && document.URL.indexOf( 'https://' ) === -1) {
    console.log("URL: Running in Cordova/PhoneGap");
    document.addEventListener("deviceready", bootstrapAngular, false);
} else {
    console.log("URL: Running in browser");
    bootstrapAngular();
}

If you run into problems with the http/https detection method, due to, perhaps, loading a Cordova app into the phone from the web, you could use the following method instead:

function bootstrapAngular() {
    console.log("Bootstrapping AngularJS");
    // This assumes your app is named "app" and is on the body tag: <body ng-app="app">
    // Change the selector from "body" to whatever you need
    var domElement = document.querySelector('body');
    // Change the application name from "app" if needed
    angular.bootstrap(domElement, ['app']);
}

// This method of user agent detection also works, though it means you might have to maintain this UA list
if (navigator.userAgent.match(/(iOS|iPhone|iPod|iPad|Android|BlackBerry)/)) {
    console.log("UA: Running in Cordova/PhoneGap");
    document.addEventListener("deviceready", bootstrapAngular, false);
} else {
    console.log("UA: Running in browser");
    bootstrapAngular();
}

Note that you still need the same bootstrapAngular function from the first example.

Why manually bootstrap AngularJS with Cordova/PhoneGap/Ionic?

Some people getting here might not know why you would want to do this in the first place. The issue is that you could have AngularJS code that relies on Cordova/PhoneGap/Ionic plugins, and those plugins won't be ready until after AngularJS has started because Cordova takes longer to get up and running on a device than the plain old Javascript code for AngularJS does.

So in those cases we have to wait until Cordova/PhoneGap/Ionic is ready before starting up (bootstrapping) AngularJS so that Angular will have everything it needs to run.

For example, say you are using the NG-Persist Angular module, which makes use of local storage for saving data on a browser, iOS Keychain plugin when running on iOS, and the cordova-plugin-file when running on Android. If your Angular app tries to load/save something right off the bat, NG-Persist's check on window.device.platform (from the device plugin) will fail because the mobile code hasn't completed startup yet, and you'll get nothing but a white page instead of your pretty app.


Manually bootstrap your Angular app:

Remove your ng-app attribute from your HTML code, so Angular doesn't start itself.

Add something like this to you JavaScript code:

document.addEventListener("deviceready", function() {
    // retrieve the DOM element that had the ng-app attribute
    var domElement = document.getElementById(...) / document.querySelector(...);
    angular.bootstrap(domElement, ["angularAppName"]);
}, false);

Angular documentation for bootstrapping apps.