Looking Back: AngularJS, Cordova and the Windows Phone Back Button

Recently we brought a Cordova-based web app which already runs successfully on iOS and Android to Windows Phone 8.1. One of the issues you will definitely come across when porting your app to this platform is the handling of the back button, an essential part of Windows Phone’s user experience.

By default, pressing the back button on Windows Phone closes your Cordova-based app, regardless of the current state. Instead, the back button should bring you back to the previous view; except on the main page, where pressing it must suspend the current app (according to the Windows Store Policies, 10.4.4):

Where applicable, pressing the back button should take the user to a previous page/dialog. If the user presses the back button on the first page of the app, then the app terminates (unless it is allowed to run in the background).

Not respecting those policies may not only lead to a rejection of your app, but also to bad ratings in the Store.

Using what’s already there

As an AngularJS developer, you will quite likely use a routing framework such as ngRoute, ui-router or the “New Router” (also known as component router) introduced in Angular 1.4. All those routers rely on the window location property of the browser. Therefore, you can simply go one step back in the app’s history in order to achieve what the back button in Windows Phone (and Android) usually does.

Windows (Phone) exposes its runtime to JavaScript apps in a library called WinJS. WinJS again exposes an event to handle back button presses. When you return true in the event handler, the event is canceled and the app continues to run, otherwise the default behavior will take place and the app suspends. We will subscribe to this event and check where we are: If we are on the main view, the app will suspend. Otherwise, we will go back one step in the history using the browser’s back stack:

if (window.WinJS && window.WinJS.Application) {
  window.WinJS.Application.onbackclick = function () {
    if (window.location.hash === '#/') {
      return false;
    }
    
    window.history.back();
    return true;
  };
}

Please note that you may have to modify this snippet so it suits your needs. If you use the HTML5 history functionality or your main view resides in a completely different route, this would not work. Instead of checking the value of window.location.hash, you could also retrieve the current route or state from your framework.

That’s it! Maybe.

If everything works now, you are done! But if you use older versions of Angular or host your app in an iframe, there’s probably some more work to do. Calling window.history.back there will always bring you back to the main state. At the time of this writing, this is an bug still open in Angular and in Internet Explorer. To work around it, you can disable the HTML5 history detection in $sniffer like so:

if (window.WinJS) {
    $provide.decorator('$sniffer', function($delegate) {
        $delegate.history = false;
        return $delegate;
    });
}

Now Angular watches and processes your location. But this change again may result in digest iteration errors (Error: 10 $digest() iterations reached. Aborting!) in Angular, for example when triggering window.history.back in digest-related operations such as functions invoked by ng-click. To address this problem, you can monkey-patch window.history.back like the following to get the navigation out of the digest cycle:

var originalBack = $window.history.back;
$window.history.back = function() {
    $timeout(function () {
        originalBack.apply($window.history);
    }, 0, false);
};

Now we are finally done and your app should run properly with full back button support on Windows Phone. Enough looking back for today. Let’s look forward.

Published by

Christian Liebel

Hey there! I am Christian Liebel from Leimersheim, Germany. I work as a consultant at Thinktecture and I am their representative at W3C. Web app development with Angular and .NET is our day-to-day business. Feel free to contact me anytime.

Leave a Reply

Your email address will not be published. Required fields are marked *