1

I'm using Cordova 3.3.1-0.4.2 and Angular 1.2.13

I need to manually bootstrap Angular once I get the Cordova 'deviceready' event.

I'm testing on a Nexus 5 with cordova run android but am having exactly the same behaviour on an iPhone.

To simplify the problem this is JS running in the global document scope. Scripts are being loaded before the closing </body> tag.


This works:

angular.bootstrap(document.getElementById("app"), ["MyApp"]);

This doesn't work:

function init(){
  angular.bootstrap(document.getElementById("app"), ["MyApp"]);
}

document.addEventListener('deviceready', function () {
  init();
}, true);

However if I add alert("init") to the init method that shows it IS running. Also alert(angular) and alert(document.getElementById("app")) show that they exist.

I don't understand why, given that init() is being called, it doesn't work when called from the EventListener callback yet it does work if called directly.

Seems weird / unintuitive.

Anyone?

Ade
  • 2,961
  • 4
  • 30
  • 47
  • `deviceready` event is used for using the cordova API – Jonathan de M. Feb 18 '14 at 01:03
  • Yes, it's `deviceready` from the Cordova API that the EventListener is listening for. – Ade Feb 18 '14 at 01:05
  • So why bootstraping angularJS when cordova is ready? Angular do not need cordova API to be ready to run, you could call the `deviceready` after bootstraping angular. – Jonathan de M. Feb 18 '14 at 01:06
  • I don't know why, but bumping it down the event loop with a `setTimeout(init,0)` might work.. – calebboyd Feb 18 '14 at 03:13
  • @calebboyd good suggestion but makes no apparent difference – Ade Feb 18 '14 at 09:37
  • @JonathandeM. When boostrapping Angular first I was finding that it was intermittently missing the `deviceready` event as it had already fired. – Ade Feb 18 '14 at 09:38
  • 1
    you should listen for device ready in your outermost angularjs controller, after bootstraping ng – Jonathan de M. Feb 18 '14 at 16:24
  • If your Angular code uses any Cordova APIs or Plugins, you need to wait for deviceready before initializing Angular. See my answer below for a simple and reliable method. – P.J. Tezza Aug 27 '14 at 21:24
  • @P.J.Tezza You don't have to if you use a promise object. – Ade Aug 28 '14 at 10:36

3 Answers3

5

The best solution I've found is to bootstrap Angular as normal and then load Cordova as a module that returns a promise, which is resolved when the device is ready.

angular.module('fsCordova', [])
.service('CordovaService', ['$document', '$timeout', '$window',  '$q',
  function($document, $timeout, $window, $q) {

    var defer = $q.defer();

    this.ready = defer.promise;

    // Backup in the case that we did not received the event
    // This seemed to be necessary with some versions of Cordova
    // when testing via 'cordova serve' in a web browser
    // but when on-device the event is received correctly
    var timoutPromise = $timeout(function() {
      if ($window.cordova){
        defer.resolve($window.cordova);
      } else {
        defer.reject("Cordova failed to load");
      }     
    }, 1200);

    angular.element($document)[0].addEventListener('deviceready', function() {
      $timeout.cancel(timoutPromise);
      defer.resolve($window.cordova);
    });  
  }
]);

Usage:

angular.module('app', ['fsCordova']).

run(['$window', function($window){
  // init Fastclick
  FastClick.attach(angular.element($window.document.body)[0]);
}]).

controller('AppCtrl', ['$scope', 'CordovaService', 
  function($scope, CordovaService){

    $scope.ready = false;

    // when cordova is ready
    CordovaService.ready.then(
      function resolved(resp) {
         $scope.ready = true;  
      },
      function rejected(resp){
        throw new Error(resp);
      }
    );
  }
]);

I've shared this bootstrap project here on GitHub

Ade
  • 2,961
  • 4
  • 30
  • 47
1

To those who actually want to manually boostrap angularjs after cordova deviceready event should use this. you can also read details of it here AngularJS + Cordova (Updated And Even Better!)

'use strict';

var CordovaInit = function() {

var onDeviceReady = function() {
    receivedEvent('deviceready');
};

var receivedEvent = function(event) {
    console.log('Start event received, bootstrapping application setup.');
    angular.bootstrap($('body'), ['c3aApp']); // Manually add and boot Angularjs 
};

this.bindEvents = function() {
    document.addEventListener('deviceready', onDeviceReady, false);
};

//If cordova is present, wait for it to initialize, otherwise just try to
//bootstrap the application.
if (window.cordova !== undefined) {
    console.log('Cordova found, wating for device.');
    this.bindEvents();
} else {
    console.log('Cordova not found, booting application');
    receivedEvent('manual')
}
};

$(function() {
console.log('Bootstrapping!');
new CordovaInit();
});

I personally chose this method because I needed to get some data from SQlite db on the device for Angularjs configuration before Angularjs loads, and loading Angularjs first seems to break my code.

I have been using this method for sometime now and everything works well.

// PLEASE DO CHECK THE ORDER OF YOUR .js FILES.

David Addoteye
  • 1,587
  • 1
  • 19
  • 31
0
  1. According to the Cordova deviceready docs, you are supposed to attach to the deviceready event in the onLoad event
  2. You maybe hitting a javascript library ordering problem

I posted the outline of a solution I came up with that does not require deferred initialization via promises and has been tested to work reliability on multiple emulators and physical iOS and Android devices on my blog.

P.J. Tezza
  • 131
  • 1
  • 4
  • That's an interesting alternative approach that demonstrably does work. However I have used the NG Cordova module approach (from the accepted answer) in a number of production projects with no problems. The `$q.defer` method is very reliable and the order of script loading does not matter due to the way Angular manages dependencies. I've put together this basic bootstrap if you're interested: https://github.com/aderowbotham/angular-cordova-bootstrap – Ade Aug 28 '14 at 09:56