Using Cordova and Ionic Framework to turn an AngularJS web application into a cross-platform mobile application.
This year our summer interns worked on an internal project to build a mobile application. They used Cordova to deploy the cross-platform HTML5 app onto multiple phone platforms, including iPhone and Android. The results looked really good, and inspired me to try making a cross-platform app out of my Angular SPA (Single Page Application) from my earlier blog post.
Setting up Cordova
Cordova’s installation guide is detailed and easy to follow, so I won’t try to reproduce that here.
Cordova uses the SDKs for each platform that you wish to target. Building an iPhone version requires an Apple Mac, which I don’t have unfortunately, but a Windows Phone and Android versions can be built in Windows using Microsoft’s SDK. I installed the Android SDK, which Cordova uses to build an Android package that can be deployed to a phone.
With the SDK installed, I used a simple npm
command to install Cordova:
npm install -g cordova
I then created a Cordova project in my solution and added Android as a target platform:
cordova create phone com.scottlogic.AngularSampleSPA AngularSPA
cd phone
cordova platform add android
With everything set up, Cordova can build the project, which creates an Android package (the .apk
file that I can simply email to my phone to install it):
cordova build
Or deploy it directly to a handset that is connected to the PC:
cordova run android
Testing with an emulator requires that you first setup a target device using the Android Virtual Device (AVD) Manager from the SDK. Then it’s as simple as the command:
cordova emulate android
At this point I had a functioning Android application, but it just showed the Cordova “ready” page.
Porting the web application
My Angular SPA uses Gulp to bundle and minify the application in the dist
folder. What if I just copied that into the content folder of my Cordova application? It can’t be that simple can it?
I decided to make a simple batch file that could build the Angular SPA, copy it over to the Cordova project, then build that too:
REM Create the distribution
cd AngularFrontEnd
call gulp
cd ..
REM Replace the Phone web folder
RD /S /Q "Phone/www"
XCOPY "AngularFrontEnd\dist" "Phone/www" /E /C /I /K
REM Build the phone apps
cd Phone
cordova build
Running that on the emulator looked like this:
It really was that simple! The application works as expected - clicking the rows in the table takes it to the view of the selected investment, and entering a search term will filter the list and update the charts.
When I replaced Cordova’s scaffolded html page, I lost the reference to Cordova’s APIs for accessing the phone’s hardware (like the camera etc.). I’m not using any of that stuff though, so the App can run without it.
Redesigning for Mobile
My Angular SPA application uses Bootstrap to control the layout, which automatically adapts to different screen sizes. On a phone-sized screen, the two charts move down below the table and the title bar changes to show a pull-down menu. This makes the application usable on a phone, but it doesn’t feel much like a mobile application.
I could improve the mobile experience by doing some more size-specific changes. For example, use the hidden-xs
class to hide elements that don’t need to be displayed on small screens. The result might be a good enough experience for mobile visitors to the website, but users expect more from installable phone apps.
Mobile view with Ionic Framework
AngularJS uses a MVW (Model-View-Whatever) pattern, so in theory we should be able to swap out the “View” part with a new View that is optimised for mobile.
Ionic is a great framework for building mobile applications, providing lots of visual elements and behaviours that users have come to expect from native mobile apps. Ionic also happens to be based on AngularJS, so can we build a mobile-specific view using Ionic, without affecting the desktop-specific view that uses Bootstrap?
I started by creating a mobile version of my index page: index-mobile.html
. It imports ionic-bundle.js
(Ionic bundled with Angular) instead of the bootstrap and angular libraries, and it references a different version of the css (mobile.css
, which is the output of the mobile.scss
Sass file that includes both the Ionic and application styles). The view container is a bit different too, as it uses Ionic directives:
<ion-pane>
<ion-nav-bar class="bar-dark" ng-controller="RouteCtrl">
<ion-nav-back-button class="button-clear" ng-click="goBack()">
<i class="ion-chevron-left"></i>
</ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view animation="slide-left-right">
</ion-nav-view>
</ion-pane>
Both index pages use the same set of javascript files (the “Model” and “Whatever” parts are common), but when we initialise the Angular application we need slightly different module dependencies. In app.js
, I configured the modules like this:
The first line tells the application where to find the partial pages (if windows.ionic
is set, we use partials-mobile
). For the Ionic version, we also create a dependency on ionic
instead of on ui.bootstrap
.
Note that the application originally used Angular’s $routeProvider
, but I had to change it to the newer $stateProvider
(which Ionic uses).
I also needed to create mobile-specific versions of the partial pages. As an example, here’s what investments-component.html
looks like with Ionic (the Bootstrap version is a table):
<ion-list>
<ion-item class="item-text-wrap investment-item"
ng-repeat="investment in investments" ng-click="showInvestment()"
ng-class="investment.returnOnInvestment < 0 ? 'down' : 'up'">
<span class="name"></span>
<span class="amount"></span>
<span class="percent"></span>
</ion-item>
</ion-list>
The only other part of the application that needed special attention was the investment-filter.js
component. In the mobile version, I wanted a search icon in the header bar that opens an Ionic “popover” window. To do that I needed to inject the $ionicPopover
service, but of course that service doesn’t exist in the desktop version. The controller needs to check whether Ionic exists, and only inject the service if it does. Everything else in the application works without modification.
The Ionic-powered mobile application looks like this:
The ‘home’ page no longer has all three components displayed at the same time, but uses Ionic’s tabbed view to show them one at a time. Clicking the search icon raises a pop-over window with a search box. Clicking one of the investments initiates a slide-left animation to show the investment details with a back button. The Ionic version looks and feels much more like a native mobile application.
Summary
Deploying a Single Page Application as a cross-platform app using Cordova turned out to be extremely easy. Most of the time was spent downloading the Android SDK.
I wanted to improve the mobile experience though, so that meant putting in the extra effort to develop a new mobile-centric View. Ionic’s use of Angular made that relatively easy. It does mean more code to develop and maintain in addition to the original application, but most of the code is shared.
I think this is a good approach to building a mobile version of a desktop SPA, but only if the mobile version needs to do the same things as the desktop version. After all, there are limits to what you can do simply by replacing the View in a Model-View-Whatever framework. Often, mobile apps need to do different things - for example, you may not want detailed analyses and reports, but instead have at-a-glance summary screens and quick-response input. Those sort of differences can be coded into the controllers (like I did with the search box pop-over), but at some point you’ll need to consider whether it’s better to build the mobile version as a separate application.