The Definitive Guide to Angular on Mobile

Mobile apps are not the next frontier for software developers, they’re already here. There are already 1.2 billion mobile web app users and that number is growing rapidly (Wikipedia). Soon, the number of mobile devices will exceed the number of people on the planet. At the rate at which the number of mobile devices is growing, it’s estimated that 5.1 billion people will be using mobile phones by 2017.

For us as app developers, it’s important that we develop for mobile technology if we want to stay relevant. With AngularJS, we have some great support for mobile, written by both the Angular team and the community.

In this article, we’re going to work through these two different ways to give our users a mobile experience for our app:

Responsive web apps

The easiest way to support mobile with Angular is by using tools we already know and love, HTML and CSS, to create a mobile-ready Angular app. Since Angular apps are already based on HTML, making our designs and interaction responsive is only a matter of building the architecture to support different devices.

Interaction

For the desktop, the ability to create interactions is already available to us through the ng-click and family directives.

Starting from Angular version 1.2.0 and on, we now have the ability to use touch events thanks to the new ngTouch module. Since ngTouch is not built in to the core Angular library, we’ll need to install the library.

Installation

Installing ngTouch can be done in several ways. The simplest way to install the ngTouch module is by downloading the source from angularjs.org.

Find the extras in the download article, and we’ll download and store the ng-touch.js file into an accessible location in our app.

Alternatively, we can use Bower to install angular-touch:

1
$ bower install angular-touch --save

Either way, we’ll need to reference the library in our index.html as a script:

1
<script src="/bower_components/angular-touch/angular-touch.js"></script>

Finally, let’s include ngTouch as a dependency in our app:

1
angular.module('myApp', ['ngTouch']);

Now we’re ready to take advantage of the ngTouch library.

ngTouch

Mobile browsers work slightly differently than desktop browsers when dealing with click events. Mobile browsers detect a tap event and then wait about 300 ms or so to detect any other taps, for instance if we’re double-tapping the device. After this delay, the browser fires a click event.

This delay can make our apps feel incredibly unresponsive. Instead of dealing with the click event, we can detect touch events instead.

The ngTouch library seamlessly handles this detection for us through the ng-click directive and will take care of calling the correct click event for us. This so-called fast click event will be called.

After the fast click has been called, the browser’s delayed click will then be called, causing a ‘double’ action. ngTouch takes care of this action for us.

Using the ngClick directive on mobile devices works exactly same way on mobile browsers as it does on desktop browsers:

1
<button ng-click="save()">Save</button>

ngTouch also introduces two new directives: the swipe directives. These swipe directives allow us to capture user swipes, either left or right across the screen. These are useful for situations where we want the user to be able to swipe to the next photo gallery photo or to a new portion of our app.

The ngSwipeLeft directive detects when an element is swiped from the right to the left, while the ngSwipeRight directive detects when an element is swiped from the left to the right.

One of the nice features the ngSwipe* directives give us is that they work both with touch-based devices as well as mouse clicking and dragging.

It is easy to use the ngSwipeLeft and ngSwipeRight directives. For instance, let’s say we have a list of emails and we want to reveal actions for each email, like the popular mobile email client MailboxApp.

We can easily implement this functionality using these swipe directives on our list of elements. When we are showing our list of emails, we’ll enable swiping in one direction to show the actions we can take on that particular mail item.

When we are showing actions for the mail item, we’ll enabled swiping in the other direction to hide the actions we can take.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<ul> <li ng-repeat="mail in emails"> <div ng-show="!mail.showActions" ng-swipe-left="mail.showActions=true"> <div class="from"> From: <span>{{ mail.from }}</span> </div> <div class="body"> {{ mail.body }} </div> </div> <div ng-show="mail.showActions" ng-swipe-right="mail.showActions=false"> <ul class="actions"> <li><button>Archive</button></li> <li><button>Trash</button></li> </ul> </div> </li> </ul>

See it

  • From: {{ mail.from }}
    To: {{ mail.to }}
    {{ mail.body }}

$swipe service

For more custom touch-based animations, we can use the $swipe service directly. The $swipe service is a service that abstracts the details of hold-and-drag swiping behavior.

The $swipe service has a single method called bind(). This bind() method takes an element on which it will bind the swipe actions as well as an object with four event handlers.

These event handlers get called with an object containing the coordinates object, like so: { x: 200, y: 300 }.

The four events handlers are handlers that handle the following events:

start

The start event is fired on either a mousedown or a touchstart event. After this event, the $swipe service sets up watches for touchmove and mousemove events. To prevent accidental swipes, these events are only fired until the total distance moved exceeds a small distance.

Once a movement has surpassed this distance, then one of two events happens:

move

The move event is called on mousemove and touchmove events only after the $swipe service has determined that a swipe is, in fact, in progress.

end

The end event is fired when we’ve finished with a touchend or a mouseup event after the move event has been fired.

cancel

The cancel event is called on either a touchcancel or when we begin scrolling after the start event instead.

For instance, we can create a directive that enables swiping between slides that might control a projector screen. To handle swiping on the mobile control, we’ll use the $swipe service to handle our custom logic for how to display the UI layer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
angular.module('myApp') .directive('mySlideController', ['$swipe', function($swipe) { return { restrict: 'EA', link: function(scope, ele, attrs, ctrl) { var startX, pointX; $swipe.bind(ele, { 'start': function(coords) { startX = coords.x; pointX = coords.y; }, 'move': function(coords) { var delta = coords.x - pointX; // ... }, 'end': function(coords) { // ... }, 'cancel': function(coords) { // ... } }); } } }]);

angular-gestures and multi-touch gestures

Angular-gestures is an Angular module that gives us the ability to handle multi-touch in our Angular apps. It is based on the very popular and well-tested Hammer.js library.

The Hammer.js library gives us a bunch of events common to touchscreen events:

The angular-gestures library enables us to use these events through Angular directives. For instance, all of these directives are available to us:

angular-gestures installation

To install the angular-gestures library in our app, we’ll need to include the gestures.js (or gestures.min.js) library in our page.

We can either download the gestures.js files directly from the GitHub page at https://github.com/wzr1337/angular-gestures, or we can use Bower to install it.

To install angular-gestures using Bower, use the following command:

1
$ bower install --save angular-gestures

Lastly, we’ll need to set angular-gestures as a dependency for our Angular app:

1
angular.module('myApp', ['angular-gestures']);

Using angular-gestures

From here, Angular gestures are really easy to use. Gestures are just Angular directives, so we’ll use them the same way we use any other directives in our app.

Let’s say that we want to allow users to rotate, pinch, and zoom photos in a photo gallery. We can use the Hammer.js library to handle this for us.

In this example, we’ll set a random translation on the element only for double taps. To do this, we’ll set up our HTML, this time using the hm-tap directive.

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="photowrapper"> <div class="cardProps" hm-tap="tapped($event)"> <div class="tradingcard"> <img src="/img/ari.jpeg" alt="" /> <span>Ari</span> </div> <div class="tradingcard"> <img src="/img/nate.jpeg" alt="" /> <span>Nate</span> </div> </div> </div>

There is nothing incredibly special about this HTML, other than the fact that we have a directive called: hm-tap. This is the angular-gestures directive that will handle our actions when the image is tapped.

The Hammer.js directives can take angular expressions, so we can call functions or run actions inside of them (just like ng-click, for example) and they also can take Hammer.js options.

In the example above, we’re calling a function that we’ll be defined on our $scope as tapped(). This function looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
$scope.tapped = function($event) { var ele = $event.target; var x = Math.floor(Math.random() * 200) + 1, y = Math.floor(Math.random() * 100) + 1, z = Math.floor(Math.random() * 6) + 1, rot = Math.floor(Math.random()*360)+1; $(ele).css({ 'transform': "translate3d("+x+"px,"+y+"px,"+z+"px)" + "rotate("+rot+"deg)" }); }

The angular-gestures library gives us access to the event through a special argument called $event. We’ll use the event’s target ($event.target) to determine which element our user clicks on and then we can go crazy and do all sorts of neat tricks with the element.

Native applications with Cordova

Cordova is a free, open-source framework that allows us to create mobile apps using standard web APIs, instead of native code. It enables us to write mobile applications using HTML, JavaScript, CSS, and AngularJS instead of needing to write Objective-C or Java (for iOS or Android, respectively).

Cordova

Cordova exposes native device access through JavaScript APIs that allow us to run device-specific operations, such as getting the native location or using the camera. It is built to support the plugin architecture so we can take advantage of Cordova community-built plugins, such as native audio access or barcode scanning plugins.

One of the benefits of using Cordova is that we can reuse our Angular app code to support the mobile environment. Of course, there are a few issues that we’ll deal with, such as performance and native component access.

Installation

Cordova itself is distributed as an npm package, so we’ll use npm to install it.

If you do not have npm installed, make sure you have node installed. For information on installing NodeJS, read the Next Steps chapter.

1
$ npm install -g cordova

Installing Cordova

The Cordova package includes a generator that will create our app and make it Cordova-ready.

Getting started with Cordova

Getting started with Cordova is simple. We’ll use the generator to create the starting point of our Cordova app. We’ll call the app GapApp.

The generator takes up to three parameters:

The directory where we’ll create the app

The ID of the project (the package name in reverse-domain style)

The package name (name of the application)

1
$ cordova create gapapp io.fullstack.gapapp "GapApp"

This line will set up a directory called gapapp (identified by the first parameter) with a package ID io.fullstack.gapapp and the project name GappApp.

Cordova file structure

The Cordova team has broken Cordova into plugins so that we don’t need to include platforms we won’t be building for (and thus making it easier to develop support for other platforms). That means that we’ll need to add to our project any platforms for which we’re interested in developing.

For this project, we’ll assume the rest of these commands are run from inside the project directory:

1
$ cd gapapp/

We’ll be building for iOS (although the process is the same for other platforms). To add iOS as a platform, simply add it to the project using the following Cordova command:

1
$ cordova platform add ios

For this command to work, we’ll need to ensure we have the iOS SDK installed using XCode. Download the iOS SDK and XCode at developer.apple.com.

Once you have that set, build the basic app:

1
$ cordova build ios

Now, due to some intricacies with Apple’s developer tools, we will have to build the app ourselves to get it to run on our local iOS simulator.

Let’s navigate to our app directory, where we’ll find the platforms directory. Inside of it, we’ll find the io/ directory that was created for us by the platform add command above.

Generated project

In XCode, open the project that we created with said command. Make sure the simulator is shown in the platform identifier at the top of XCode.

Build in XCode

Click run.

Once you have done so, we should see the basic Cordova app start to run in our simulator.

Barebones Cordova app

Development workflow with Cordova

Cordova powers the PhoneGap project, which has been accepted into the Apache Foundation. The project itself includes a command-line tool that we’ll use to interact with our native app, from creation to deployment.

Platforms

At this point, we’ve created our app and added a platform (in this case, iOS).

Available platforms for the Cordova app vary depending on which development environment we’re using. On a Mac, the available platforms are:

For a Windows machine, we can develop for the following platforms:

If we forget which platforms are available, we can run the platforms command to check which are available and installed:

1
$ cordova platforms ls

To add a platform, we can use the platform add command (as we’ve done above):

1
$ cordova platform add android

To remove one, we can use the rm or remove command:

1
$ cordova platform rm blackberry10

Plugins

Cordova is built to be incredibly modular, with the expectation that we will install all of the non-core components with the plugin system. To add a plugin to our project, we’ll use the plugin add command:

1
$ cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-geolocation.git

We can list the current plugins that we have installed using the plugins ls command:

1
2
$ cordova plugins ls [ 'org.apache.cordova.geolocation' ]

Finally, we can remove a plugin using the plugin rm command:

1
$ cordova plugins rm org.apache.cordova.geolocation

Building

By default, Cordova creates a skeleton project that houses the web files in www/ directory in the project directory. When Cordova builds the project, it copies these files and places them in their platform-specific directories.

To build the app, we’ll use another Cordova command, the build command:

1
$ cordova build

Without specifying any platform to build for, this command will build for all of the platforms we’ve listed in our project.

We can limit the scope by building only for specific platforms, such as:

1
2
$ cordova build ios $ cordova build android

The build command will ensure that the necessary platform-specific code is set so our app can be compiled. In effect, we’re doing the same thing as calling cordova prepare && cordova compile.

Emulating and running

Cordova also makes it possible to run an emulator in order to simulate running the app on a device. Doing so is, of course, only possible if an emulator is installed and set up on our local development environment.

Assuming our emulator is set up in our development environment, we can tell Cordova to launch and install our app in our emulator:

1
$ cordova emulate ios

For iOS, we may have to build the project (as we did above) using XCode if the emulator environment is not set up on our machine.

It’s also possible to run the application on a particular device by using the run command instead. The run command will launch the application on a device or on the emulator if no device is found and available.

1
$ cordova run ios

In development

It can be cumbersome to make a change to one part of our app and need to recompile the app to see the changes reflected in our app. To help speed the process of developing the web app side of the app, we can use the serve command to serve a local version of our www/ folder to a web browser.

1
2
3
4
$ cordova serve ios Static file server running at => http://0.0.0.0:8000/ CTRL + C to shutdown

Now, we can use our web browser and navigate to the URL: http://localhost:8000/ios/www/index.html. Our app’s www/ folder is being served through HTTP, so we can build it and watch it change as we make changes to the app.

When we make changes, we’ll need to make sure we rebuild the app:

1
$ cordova build ios

Building using Safari

Angular Cordova Service

When our Cordova app is ready, the device has connected, and everything is ready to go, Cordova will fire the browser event called deviceready.

With Angular, we can either bootstrap the app after this event has been fired or we can use promises to handle our logic after the deviceready event has been fired.

To bootstrap the app after we’ve received the deviceready event, we’ll need to set an event listener for the event and then manually call bootstrap on our app:

1
2
3
4
5
6
7
angular.module('myApp', []); var onDeviceReady = function() { angular.bootstrap( document, ['myApp']); } document.addEventListener('deviceready', onDeviceReady);

We prefer to use an alternative method of listening for the deviceready event that uses promises to set up execution bindings for after the deviceready event has been fired.

We’ll set up an Angular module that will listen for the deviceready event. We’ll use a service that will listen for the deviceready event and resolve our promises depending on whether the event has been fired.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
angular.module('fsCordova', []) .service('CordovaService', ['$document', '$q', function($document, $q) { var d = $q.defer(), resolved = false; var self = this; this.ready = d.promise; document.addEventListener('deviceready', function() { resolved = true; d.resolve(window.cordova); }); // Check to make sure we didn't miss the // event (just in case) setTimeout(function() { if (!resolved) { if (window.cordova) d.resolve(window.cordova); } }, 3000); }]);

Now, we’ll set the fsCordova as a dependency for our module:

1
2
3
angular.module('myApp', ['fsCordova']) // ...

We can use the CordovaService to determine if Cordova is, in fact, ready, and we can set our logic to depend upon the service being ready:

1
2
3
4
5
6
7
angular.module('myApp', ['fsCordova']) .controller('MyController', function($scope, CordovaService) { CordovaService.ready.then(function() { // Cordova is ready }); });

Including Angular

With the bare Cordova app, we only have a bare JavaScript app that hides and displays the JavaScript view in js/index.js.

We can introduce Angular into the workflow very simply. As we are building a native app, including Angular from a CDN is not ideal; instead, we’ll include the necessary components directly into the app.

We can use Bower for more complex setups, but for the time being, we’ll keep it simple.

To get our Angular app building, we’ll need to download Angular from angularjs.org and store it in a directory accessible by our index.html. We recommend www/js/vendor/angular.js.

Once that’s set, we can start building our Angular app. We’ll need to include the JavaScript file in our www/index.html.

1
<script type="text/javascript" src="js/vendor/angular.js"></script>

Now, we can replace all of the contents of the js/index.js file with our Angular app and develop our app like normal.

Development workflow

When building our app, we’ll use the following workflow:

This flow, although somewhat cumbersome, is how we’ll edit our app.

If our app doesn’t rely on the Cordova platform, we can edit outside of the simulator and in our web browser (e.g., Chrome). In this case, we can work specifically with building our app, instead of needing to rebuild and redeploy the app.

We have included a bunch more information about more mobile workflows in our upcoming book, ng-book: The Complete Book on AngularJS.

It’s clear that mobile is here to stay and Angular is a very powerful framework that can already get us incredibly far in building mobile experiences for our users. As we’ve seen, we can deliver both mobile web experiences as well as native apps using AngularJS.

Get the weekly email all focused on AngularJS. Sign up below to receive the weekly email and exclusive content.
We will never send you spam and it's a cinch to unsubscribe.

Download a free sample of the ng-book: The Complete Book on AngularJS

ng-book: The Complete Book on AngularJS is the canonical AngularJS book available today.

It's free, so just enter your email address and the PDF will be sent directly to your inbox. Mailchimp can take up to an hour to deliver the free sample chapter, but if you don't receive it within the hour, send us an email and we'll manually send them to you!

We'll send you updates about the book, when it updates and other free content.

We will never send you spam and it's a cinch to unsubscribe.

Comments

comments powered by Disqus