Nowadays in the realm of hybrid mobile application development there is a variety of available frameworks worth having a look at.

In the following tutorial I’d like to demonstrate the development life-cycle for a complete mobile application using Ionic, Cordova and AngularJS (and others) covering every step from the initial project setup, creating Angular Controllers, Directives, adding Cordova Plug-Ins, running and testing the application in the browser and emulator up to finally running the application on an Android device and assembling files for a release.

Finally I’d like to show some special features from the Ionic ecosystem like the Ionic Cloud and running our applications using the IonView app.

mobile app mockup
Figure 1. Mockup of our mobile search app

What we’re going to build

The main functionality of our application is to search for articles from my blog at www.hascode.com by a given tag that the user enters into a search field in the first tab of the tabular layout used.

If blog articles are found, they should be rendered in a list displaying their featured image, their title and an excerpt of their content and – of course – they should link to the original article on the website.

In the second tab, some basic information about the plug-in and its author is display .. the classical "about" dialogue.

mobile app mockup
Figure 2. Mockup of mobile search app

The mockup above gives a first impression of the user interface of our mobile application.

Installing Ionic and Cordova

Since we’re going to build our application with Ionic and Cordova, we should install both first ;)

We need to have nodejs installed – if it’s not installed yet, we should visit the nodejs download site, download and install nodejs -  it should take a few minutes only.

Now we’re ready to install Ionic and Cordova using the nodejs package manager (npm):

npm install -g cordova ionic

That’s all for now and we may start with the fun stuff…

Creating a new Project by Template

First of all we need to create a new Ionic project. Luckily for us the console client allows us to bootstrap a new project with ease and in addition we may choose from three available pre-set templates: blank,tabs and sidemenu.

As we’re going to build a tabbed application, we’re using the tabs template as a starting point and doing so we’re able to create our initial project using the following command:

$ ionic start hascode-tag-wizard tabs

This creates a directory named  hascode-tag-wizard that contains everything we need including bower configuration, gulp tasks, sample templates, working angular routing and module configuration, separated angular services, controllers and a lot more..

Running the Sample Application in the Browser

At this point all we have is a sample application. To run this application use the following command to start a web-server, start a new browser instance and listen for changes in our project’s file system. If we change files in our project, the browser is automatically reloaded – nice.

$ ionic serve
Running dev server: http://localhost:8100
Running live reload server: http://localhost:35729
Watching : [ 'www/**/*', '!www/lib/**/*' ]
Ionic server commands, enter:
  restart or r to restart the client app from the root
  goto or g and a url to have the app navigate to the given url
  consolelogs or c to enable/disable console log output
  serverlogs or s to enable/disable server log output
  quit or q to shutdown the server and exit

We should press ‘c‘ here to enable client debugging – it prints the output from console.log() in the application to the console.

Here is an example from a later version of our application:

ionic $ c
Console log output: enabled
Loading: /?restart=644673
ionic $ 0     174983   log      searching articles for tag tdd
1     175307   log      hits received: 13
2     188088   log      searching articles for tag javaee
3     188425   log      hits received: 11

If we’re opening the sample application at http://localhost:8100 (or a higher port if 8100 was already in use) it should look similar to this one:

sample app in the browser
Figure 3. Rendering the sample application in the browser

Now we’re ready to implement our real application.

Enabling the Android Platform

To enable the application for a designated target platform, simply run ionic platform add. I’m using the Android platform throughout the tutorial but I’m positive, that building for the iPhone (with an existing license etc.) or other possible platforms should be working without a problem, too (I hope so).

$ ionic platform add android

Angular Module Configuration

Next we’re doing some basic setup like modifying our index.html to include all style-sheets and scripts we need in their correct order.

The following code shows our index.html. We have added an additional file for our Angular directives named directives.js in there.

Besides this, our navigation bar is added here and we’re referencing our Angular module named hascodeTagWizard that’s going to be created in the next step.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>

    <link href="lib/ionic/css/ionic.css" rel="stylesheet">
    <link href="css/style.css" rel="stylesheet">

    <script src="lib/ionic/js/ionic.bundle.js"></script>

    <script src="js/app.js"></script>
    <script src="js/controllers.js"></script>
    <script src="js/services.js"></script>
    <script src="js/directives.js"></script>

    <script src="cordova.js"></script>
</head>
<body ng-app="hascodeTagWizard">

<ion-nav-bar class="bar-stable">
    <ion-nav-back-button>
    </ion-nav-back-button>
</ion-nav-bar>

<ion-nav-view></ion-nav-view>
</body>
</html>

Angular Module Configuration and Navigation Routing

In the following file named app.js we’re specifying our Angular module. Besides some boilerplate code from the Ionic framework, we’re adding our navigation rules in the module configuration to specify link-patterns and their assigned view-templates and controllers.

The search-tab is bound to the SearchController, the about-tab to the AboutController and if a link does not match any rule, the search tab is rendered as a fallback.

/* global angular, console, cordova, StatusBar */
angular.module('hascodeTagWizard', ['ionic'])

    .run(function ($ionicPlatform) {
        "use strict";
        $ionicPlatform.ready(function () {
            if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
                cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
            }
            if (window.StatusBar) {
                StatusBar.styleLightContent();
            }
        });
    })

    .config(function ($stateProvider, $urlRouterProvider) {
        "use strict";

        $stateProvider
            .state('tab', {
                url: "/tab",
                abstract: true,
                templateUrl: "templates/tabs.html"
            })

            .state('tab.search', {
                url: '/search',
                views: {
                    'tab-search': {
                        templateUrl: "templates/tab-search.html",
                        controller: 'SearchController'
                    }
                }
            })

            .state('tab.about', {
                url: '/about',
                views: {
                    'tab-about': {
                        templateUrl: "templates/tab-about.html",
                        controller: 'AboutController'
                    }
                }
            });
        $urlRouterProvider.otherwise('/tab/search');
    });

Tab Views Configuration

In the next step we’re configuring our tab container and the icons used for its different states in the external file named tabs.html.

<ion-tabs class="tabs-icon-top tabs-color-active-positive">

    <ion-tab title="Search" icon-off="ion-ios-search" icon-on="ion-ios-search-strong" href="#/tab/search">
        <ion-nav-view name="tab-search"></ion-nav-view>
    </ion-tab>

    <ion-tab title="About" icon-off="ion-ios-information" icon-on="ion-ios-information" href="#/tab/about">
        <ion-nav-view name="tab-about"></ion-nav-view>
    </ion-tab>
</ion-tabs>

Ionic Icon Search – Ionicons

The icons used above have been selected from the Ionicons website. It’s a nice tool to find and select an icon needed in no time.

ionicons icon search 1024x793
Figure 4. Icon search on ionicons.com

Implementing the Search Function

Finally we’re starting to implement the main functionality of our application – the tag search.

Search View

We’re beginning with out view template named tab-search.html that is bound to the SearchController as specified in our route declaration above.

There is not much to say, however we’ve got an input field that starts the search when the enter button is pressed. If the search term entered changes, the search result list is reset.

The search results are rendered using an ng-repeat and a custom directive to display each blog article found (the directive is described in the next chapter).

__

<ion-view view-title="hasCode.com Tag Search">
    <ion-content class="padding">
        <div class="list list-inset">
            <label class="item item-input">
                <i class="icon ion-search placeholder-icon"></i>
                <input type="text" placeholder="Search articles by tag" ng-model="term" ng-keyup="$event.keyCode == 13 && searchTerm()" ng-change="resetResults()">
            </label>
            <h3 ng-show="showResults">{{hits.length}} Hits for "{{term}}"</h3>
            <div class="list" ng-repeat="hit in hits">
                <blogarticle data="hit"/>
            </div>
        </div>
    </ion-content>
</ion-view>

Blogarticle Directive

Our directive named blogarticle renders a single blog-article with its featured image, title and an excerpt and manages clicks on the item (more about this later in "Using the InApp Browser").

/* global angular, console */
angular.module('hascodeTagWizard')

    .directive('blogarticle', function () {
        "use strict";

        return {
            restrict: 'E',
            scope: {
                article: '=data'
            },
            link: function (scope, element, attrs) {
                scope.browse = function (url) {
                    console.log('opening link: '+url);
                    window.open(url, '_blank', 'location=yes');
                };
            },
            template: '<a class="item item-thumbnail-left item-text-wrap" ng-click="browse(article.url)">' +
                '<img ng-src="{{article.image}}">' +
                '<h2>{{article.title}}</h2><p>{{article.excerpt}}</p></a>'
        };
    })
;

Search Controller

The search controller delegates search requests to the search service and manages the control flow and state of our views.

angular.module('hascodeTagWizard')
    .controller('SearchController', function (searchService, $scope) {
        "use strict";

        $scope.term = '';
        $scope.hits = [];
        $scope.showResults = false;

        $scope.searchTerm = function () {
            searchService.searchByTag(this.term).then(function(response){
                $scope.hits = response.data || [];
                console.log('hits received: '+$scope.hits.length);
                $scope.showResults = true;
            });
        };

        $scope.resetResults = function () {
            $scope.showResults = false;
            $scope.hits = [];
        };
    });

Same-Origin-Policy (SOP) and Cross-Origin-Resource Sharing (CORS)

To work around the same-origin-policy restriction the remote back-end sends the following header to enable cross-origin-resource-sharing:

Access-Control-Allow-Origin: *

Server Side Response

This is a sample response from my blog (shortened):

[
   {
      "title":"A short Introduction to ScalaTest",
      "url":"https://www.hascode.com/?p=980",
      "image":"http
://www.hascode.com/wp-content/uploads/2013/01/infinitest.png",
      "excerpt":"ScalaTest is an excellent
 framework to write concise, readable tests for your Scala or Java code with less effort. [..]"
   },
   {
      "title":"BDD Testing with Cucumber
, Java and JUnit",
      "url":"https://www.hascode.com/?p=1162",
      "image":"https://www.hascode.com/wp-content
/uploads/2014/12/cucumber-second-test-in-junit-runner.png",
      "excerpt":"[..]"
   }
]

Search Service

The search service manages the communication to the remote server and returns a promise used by the controller.

/* global angular, console */
angular.module('hascodeTagWizard')

    .factory('searchService', function ($http) {
        "use strict";

        var blogUrl = 'http://THEBLOGURL/byTag.php?tag=';

        return {
            searchByTag: function (tag) {
                var searchUrl = blogUrl + tag;
                console.log('searching articles for tag ' + tag);

                return $http({method: 'GET', url: searchUrl});
            }
        };
    });

The About Tab

The about-tab simply displays the ugly logo from my blog, a link to my blog and the current year.

About View

This is the view template in a file named tab-about.html bound to the AboutController.

<ion-view view-title="About" ng-controller="AboutController as about">
    <ion-content class="padding">
        <div class="card">
            <div class="item item-divider">
                <h2>hasCode.com Tag Search App</h2>
            </div>
                <div class="item item-image">
                    <img src="img/icon.png">
                </div>
            <div class="item item-divider">
                <a ng-click="about.browseBlog()">{{about.year}} Micha Kops /  www.hascode.com</a>
            </div>
        </div>
    </ion-content>
</ion-view>

About Controller

This is the designated controller, nothing special here.

/* global angular, console */
angular.module('hascodeTagWizard')

    .controller('AboutController', function () {
        "use strict";

        this.year = new Date().getFullYear();

        this.browseBlog = function(){
            window.open('https://www.hascode.com/', '_blank', 'location=yes');
        };
    })
;

Testing the final app in the browser

Running ionic serve again, we’re now able to test the application in a browser.

The result should look similar to this one:

Testing the final application in the browser

tag search wizard in action inbroser

Generating App Icon and Splash Screen Images

Another nice feature of the Ionic framework is the possibility to generate the different images in different sizes needed to display the application icon and splash screen for the different target platforms.

Simply add the source image using a format like PNG, Photoshop psd or Illustrator ai files, copy them to the resources directory e.g. replacing the existing icon.png or splash.png and run the following command and the new app icon or splash screen is added to our application:

$ ionic resources
Ionic icon and splash screen resources generator
[..]
generating splash android drawable-port-xxxhdpi-screen.png (1280x1920)...
generating splash android drawable-port-xxhdpi-screen.png (960x1600)...
generating splash android drawable-port-xhdpi-screen.png (720x1280)...
✓ splash android drawable-port-xhdpi-screen.png (720x1280) generated
generating splash android drawable-port-hdpi-screen.png (480x800)...
✓ splash android drawable-port-hdpi-screen.png (480x800) generated
generating splash android drawable-port-mdpi-screen.png (320x480)...
✓ splash android drawable-port-mdpi-screen.png (320x480) generated
generating splash android drawable-port-ldpi-screen.png (200x320)...
✓ splash android drawable-port-xxhdpi-screen.png (960x1600) generated
generating splash android drawable-land-xxxhdpi-screen.png (1920x1280)...
✓ splash android drawable-port-ldpi-screen.png (200x320) generated
generating splash android drawable-land-xxhdpi-screen.png (1600x960)...
✓ splash android drawable-land-xxhdpi-screen.png (1600x960) generated
[..] etc ...

More information about icon and splash screen image generation can be found in the Ionic documentation here.

Adding Cordova Plugins: InAppBrowser

To open external links using an in-app-browser we need to add the Cordova plug-in of the same name.

Installing Cordova plug-ins is easy and the following command is everything we need to to to add the in-app-browser plug-in:

$ ionic plugin add org.apache.cordova.inappbrowser

From now on we may use this browser when opening links like this:

window.open(url, '_blank', 'location=yes');

Running with the Android Emulator

For near-real experience and the Android SDK installed, we’re now able to run our application using the Android emulator by running the following command:

$ ionic emulate android

This is what the about-tag looks like in the Android emulator.

The "About" view running on the Android emulator

about view in android emulator

And this is the search result for the term “tdd” run in the emulator:

search view in android emulator
Figure 5. The "search" view running on the Android emulator

You may use logcat to receive the logs as described in the following article of mine here.

Running on a connected Android Device

Deploying the application for test on a real Android device is really easy.

All you need to do is to enable “USB debugging” in the developer settings and run the following command:

$ ionic run android

This is a screenshot taken from a Sony Xperia Z3 compact with Android Lollipop:

search view on android lollipop 576x1024
Figure 6. Search app running an an Android device

Again you may use logcat to receive the logs as described in the following article of mine here.

Packaging for Android

To build an an Android apk-file we simply need to run the following command.

More detailed information about the release build process and apk-signing can be found in the Ionic Documentation here.

$ ionic build android
Updated the hooks directory to have execute permissions
running cordova build android
[..]
-build-setup:
[getbuildtools] Using latest Build Tools: 21.1.2
     [echo] Resolving Build Target for MainActivity...
[gettarget] Project Target:   Android 5.0.1
[gettarget] API level:        21
     [echo] ----------
     [echo] Creating output directories if needed...
     [echo] ----------
     [echo] Resolving Dependencies for MainActivity...
[dependency] Library dependencies:
[dependency]
[dependency] ------------------
[dependency] Ordered libraries:
[dependency]
[dependency] ------------------
     [echo] ----------
     [echo] Building Libraries with 'debug'...

[..]
-package:
[apkbuilder] Found modified input file
[apkbuilder] Creating MainActivity-debug-unaligned.apk and signing it with a debug key...
[..]

BUILD SUCCESSFUL
Total time: 9 seconds
Built the following apk(s):
    /data/project/hascode-tag-wizard/platforms/android/ant-build/MainActivity-debug.apk

App Distribution using Ionic.io

Another nice feature of using Ionic is the possibility to upload an application to the cloud, share it there, manage deployments of these applications and install them on our Android/ios Devices using IonView to bypass the registration process in the native application marketplaces.

First of all we need to register an account at https://apps.ionic.io/signup

Afterwards we’re able to login by using the credentials from the step above by running the following command from our project’s directory:

$ ionic login

Afterwards we’re able to upload our application to the cloud and the application gets an identifier assigned:

$ ionic upload

Now we’re able to manage our application on the Ionic Apps website: https://apps.ionic.io

Sharing Apps

Now that our application is online, we may share the app using the sharing feature as show in the following screenshot:

ionicio sharing app 1024x793
Figure 7. Sharing Apps with Ionic.io

App Deployment

Another feature is the possibility to switch between different existing versions of our application for deployment.

The following screen-shots demonstrates this feature:

ionicio app deployment 1024x793
Figure 8. Managing app deployments with Ionic.io

Ion View App

The IonView app allows us to bypass the registration process for our application on the native markets. A user only needs to download the IonView app on Google Play or Apple’s AppStore and may download our application from the Ionic.io cloud.

We should be aware though that this application is still beta and depending on the APIs we’re using in one of our applications we might encounter problems when using this bypass solution – nevertheless it is an interesting solution and I wanted to include it here.

This is a screenshot when running IonView on my Android device with our search app downloaded and installed.

search app in ionview
Figure 9. Search app downloaded in IonView

Tutorial Sources

Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Git:

git clone https://github.com/hascode/hascode-tag-search-app.git

Other Ionic/Mobile Development Articles of mine

Please feel free to have a look at other articles of mine about this topic here:

Article Updates

  • 2015-05-05: Information about generating app icons and splash screens added.

  • 2015-11-17: Links to other Ionic tutorials of mine added.