The main Jibebuy  web app is a single page web app based on AngularJS.  Inspired by reading The Definitive Guide to Angular on Mobile, I decided to spend 1 day porting Jibebuy to Cordova. After just a few hours, I was able to login to Jibebuy on my Android phone! After a couple of days, I had a quirky, but mostly working app that could run on both iOS and Android. After that major success, I spent the next several weeks putting a lot of effort small but not inconsequential details like scrolling, properly sizing elements for different devices and handling orientation changes to make sure we had a great mobile experience. In addition, I discovered some important implementation issues – read below for details.


Handling Device Ready

Contrary to the instructions given in in both the Cordova documentation AND “The Definitive Guide to Angular on Mobile”, here is my hard won advice for properly handling the all important device ready event and initializing AngularJS:

  • Include cordova.js as your LAST script include (not first as instructed elsewhere!)
  • Handle deviceready just before </body> as shown below:
    <script>
        function handleDeviceReady() {
            console.log("handleDeviceReady running...");

            console.log("handleDeviceReady configuring StatusBar...");
            StatusBar.overlaysWebView(false);
            StatusBar.backgroundColorByHexString('#009999');
            StatusBar.show();

            console.log("handleDeviceReady OAuth.initialize...");
            OAuth.initialize('BlahBlahBlahBlah');

            console.log("handleDeviceReady angular.boostrap...");
            angular.bootstrap(document, ['list']);

            console.log("handleDeviceReady hiding splashscreen...");
            navigator.splashscreen.hide();

            console.log("handleDeviceReady checking network connection...");
            if (navigator.network.connection.type == Connection.NONE) {
                navigator.notification.alert("No Internet connection", function() {}, 'Error Loading');
            }

            console.log("handleDeviceReady complete.");
        }

        document.addEventListener('DOMContentLoaded', function() {
            console.log("Adding deviceready event listener...");
            document.addEventListener('deviceready', handleDeviceReady, false);

            console.log("Configuring jquery plugins...");
            $(document).on('click', 'a[href^=http], a[href^=https]', function(e) {
                e.preventDefault();
                var $this = $(this);
                window.open($this.attr('href'), '_blank', 'location=yes', cordova_login_token);
            });
            $('.navbar .btn-navbar').css('display', 'block');

            $('.read-more').readmore();
            $('abbr.timeago').timeago();
            $("a[href='#top']").click(function() {
                $("html, body").animate({ scrollTop: 0 }, "slow");
                return false;
            });
        }, false);
    </script>

Note the following about the above code:

  • No weirdness using promises to postpone initialization of AngularJS – just call angular.bootstrap in the deviceready handler (which is attached in the DOMContentLoaded handler)!
  • I’ve included initialization code for other commonly used components such as jQuery controls and OAuth (more about these below).
  • I’m showing you the best way I’ve found to show and hide the status bar if you want a splash screen

OAuth for Social Logins

Like a lot of apps, Jibebuy lets you login with Facebook, Twitter, etc.. Supporting OAuth and OAuth2 is straightforward (if tedious) for a web app, but these protocols weren’t designed for mobile apps. I looked into various social auth plugins for Cordova, but the serious ones required installing native SDKs for every platform (so developers would have to install and maintain Facebook SDK for both iOS and Android, for example), and the ones I looked at didn’t always support both iOS and Android with the same API. I ended up outsourcing to OAuth.io. Unfortunately, OAuth.io doesn’t completely insulate you from the tedious details of configuring app keys, URLs, etc. for every provider, but, at least from your app’s perspective, there is a single API to deal with.

Scrolling and other Touch Issues

Much has already been written about the 300ms click delay and I don’t have anything truly new to contribute on the subject. My advice is to test whatever solution you choose carefully on all your supported devices and platforms. Fortunately, it is easy to handle touch events in AngularJS or whatever.

I wanted scrolling and toolbars to work a particular way in our main window. This ended up taking a lot of time to get working OK (still not perfect) on both iOS and Android. One important scrolling related tip for AngularJS users: Make sure you avoid AngularJS digests if you are trying to handle rapidly fired events like scrolling! I used simple jQuery to handle scrolling in JavaScript.

Resize and Orientationchange Events

On iOS under Cordova, you won’t get a resize event when the orientation changes, but you do get an orientationchange event. I’m using Twitter Bootstrap CSS for responsive layout, but I still have some images that need to be resized when the window size changes. Here is the code I ended up with (don’t hate!):

    // The block of code below can be used in any scope to call $apply when the window is resized
    var timeoutPromise = null;
    var window = angular.element($window);
    var windowWidth = 0;
    window.on('resize orientationchange', function(e) {
        // console.log('resize or orientationchange: ' + e.type);
        if (timeoutPromise != null)
            $timeout.cancel(timeoutPromise);
        timeoutPromise = $timeout(function() {
            var newWidth = window.width();
            if (!website.isiOs() && windowWidth == 0) {
                windowWidth = newWidth;
                return;
            } else if (windowWidth == newWidth) {
                return;
            } else {
                windowWidth = newWidth;
                // console.log('resize or orientationchange: broadcast');
                $rootScope.$broadcast('newWindowWidth', newWidth);
            }
        }, 250);
    });

Twitter Bootstrap / Angular Boostrap Modals

In addition to Twitter Bootstrap, I also use Angular UI Bootstrap. Both of these libraries supply a modal component for creating popups. OK, I know modal dialogs are old fashioned, but I find that sometimes, the old, simple ways still work. The modal component supplied by both libraries doesn’t work well on mobile – or should I say doesn’t work at all. Online via Google, I found a lot of possible workarounds. Some were a couple of lines of CSS and others were a couple hundred lines of JavaScript. Though I spent a lot of time on these various ideas, none of them pleased me. In the end, I hacked up the Angular UI Bootstrap modal dialog code to get what I wanted. Since the result is kind of a mess and may eventually be rendered obsolete (Twitter Bootstrap 3.0 used a similar strategy to mine for modal dialogs on mobile), I’m not posting any code. But, if you need something like this, let me know and I’ll be happy to share my hacks.