Blog

- May 30, 2016

Mobile applications are specific since they need to handle devices of limited size. For this reason, they leverage specific UI mechanisms and gestures. In order to implement efficient applications of this kind, we need to follow proven patterns described in the following links :

We won’t go further here but we will describe all along the article how to implement some of them within an Ionic 2 application.

Getting started with Ionic 2

Ionic is a comprehensive open-source framework to develop hybrid mobile applications. It’s built on top of Angular 2 for its version 2 and Apache Cordova. It provides a set of components, tools and services to develop and test such applications using Web technologies.

Generating application skeleton

Like with its version 1, Ionic provides a command line tool to generate the application skeleton.

$ ionic start

It’s also possible to use TypeScript for the application. The –ts property must be added to the previous command line.

There are several modes to then launch our application. This first one consists of executing within a browser. Recall that an Ionic 2 application is an Angular 2 one.

$ ionic serve --labs

We can also emulate the application using Android emulators for example. The first command starts the default emulator of the Android SDK:

$ ionic run android

To use another emulator like Genymotion, we need to start it and then call the following command:

$ ionic emulate android

Let’s now have a look at the content of the generated application.

Application overview

Ionic 2 relies on Angular 2 so it follows the same principle. The main difference is that you don’t need to bootstrap your application. To specify the class for the application, we just need to add a @App decorator to it.

At this level, you can define:

  • A template that contains the content of the view implemented with HTML and a set of components provided by Ionic. At this level, either the template attribute or the templateUrl one can be used. The second one allows you to externalize the HTML code from the JavaScript one.
  • The application providers to make possible to inject various instances into your components and services.
  • A configuration object to define global or per platform configuration properties.

Here is the content of a simple application class:

@App({
  template: `
    <ion-nav id="nav" [root]="rootPage">
    </ion-nav>
  `,
  config: {
    (...)
  },
  providers: [ (...) ]
})
export class BookApp {
  static get parameters() {
    return [[Platform]];
  }

  constructor(platform) {
    this.rootPage = SomePage;

    platform.ready().then(() => {
      StatusBar.styleDefault();
    });
  }

  openPage(page) {
    this.rootPage = page;
  }
}

The rootPage property allows to specify the current page to display.

Pages correspond to classes with the @Page decorator. The associated templates leverage the Ionic 2 components and directives to display appropriate contents.

@Page({
  template: `
    <ion-navbar *navbar>
      <ion-title>
        Title
      </ion-title>
    </ion-navbar>

    <ion-content>
      (...)
    </ion-content>
  `
})
export class SomePage {
}

Now we have all the foundations of our Ionic 2 application, let’s link it with a backend.

Linking with the mobile backend

Most of time a mobile application needs to interact with a remote backend for its data and business processing. The remote backend is often provided as a Web API and we will use API Cloud for this article. Because this platform relies on REST principles, what we will describe here applies to any RESTful service.

Since Ionic 2 is based on Angular 2, we need to specify the HTTP_PROVIDERS array. Since Ionic 2 doesn’t need to bootstrap a component, you need to specify it into the application providers of the application component.

The thing to notice here is that the generated Ionic 2 project uses ES6 and not TypeScript. For this reason, we can’t use types of constructor parameters since it’s not allowed with ES6. In this case, we need to define a static getter for the parameters property.

static get parameter() {
  return [[Http]];
}

Notice that it’s possible to use TypeScript to implement the application, as described previously. In this case, it’s possible to use types at the level of method parameter, class properties, …

We won’t go further here. If you need more hints about the way to use the HTTP support of Angular 2, you can have a look at these articles:

For this article, we will use a Web API implemented in the APISpark platform to manage books.

image07

image03

Let’s now have a look at how to leverage menus and tabs.

Organizing navigation

If you already use a mobile application (and we’re sure you do!), having a sidebar you can display with the swipe gesture is something classical as well as tabs. Ionic 2 obviously provides such features straight out of the box. Let’s start with menus.

Left & right menus

The first thing consists of extending the application template to add the block that defines the menu content leveraging the ion-menu component. This component allows you to define advanced content with toolbar and all list kinds supported by Ionic 2.

This must be done in the application template as described in the following snippet:

@App({
  template: `
    <ion-menu side="left" [content]="content">
      <ion-toolbar>
        <ion-title>Menu</ion-title>
      </ion-toolbar>

      <ion-content>
        <ion-list>
          <button menuClose ion-item (click)="openPage(booksPage)">
            Books
          </button>
          <button menuClose ion-item (click)="openPage(authorsPage)">
            Authors
          </button>
        </ion-list>
      </ion-content>
    </ion-menu>

    <ion-nav id="nav" #content [root]="rootPage">
    </ion-nav>
  `,
  (...)
})
export class BookApp {
  (...)
}

There are several things to remark here. First the ion-menu component needs to reference the ion-nav one using its content attribute. This can be done by adding a local variable on the ion-nav component.

The second thing is the use of the menuClose directive on buttons to automatically close the menu when the button is clicked.

The menu can be opened using the swipe gesture or by adding a button with the menuToggle directive in the page toolbar as described below:

<ion-navbar *navbar>
  <button menuToggle>
    <ion-icon name="menu"></ion-icon>
  </button>
  <ion-title>
    Title
  </ion-title>
</ion-navbar>

<ion-content class="page3">
(...)
</ion-content>

Here is the result.

image00

Tabs

Tabs are also a great way to the navigation easy into a mobile application. With Ionic 2, this is possible using the ion-tabs component. It can be used internally to a page to define a group of pages and actions to display them.

@Page({
  template: `
    <ion-tabs>
      <ion-tab [root]="tab1Root" tabTitle="Tab 1" tabIcon="pulse"></ion-tab>
      <ion-tab [root]="tab2Root" tabTitle="Tab 2" tabIcon="chatbubbles"></ion-tab>
      <ion-tab [root]="tab3Root" tabTitle="Tab 3" tabIcon="cog"></ion-tab>
    </ion-tabs>
  `
})
export class BooksPage {
  (...)
}

When clicking on the corresponding icons, pages will be display within the books one.

image01

Displaying lists

Now we have defined global elements of your application like menu and tabs, it’s time to display a list into a page.

Loading the list

To load the list in the page, we can take advantage of the HTTP support of Angular 2. To execute the call, we can leverage the onPageWillEnter lifecycle hook. This way the list will be reloaded each time the page is displayed.

When the response of the request is received within the callback registered using the subscribe method, we set its payload into a books property. We will leverage this property in the template to display the list of books.

@Page({
  (...)
})
export class BooksPage {
  constructor(http) {
    this.http = http;
  }

  static get parameters() {
    return [[Http]];
  }

  onPageWillEnter() {
    this.http.get('https://bookapi.apispark.net/v1/books/')
      .map(res => res.json())
      .subscribe(books => {
        this.books =books;
      });
  }
}

List items

Ionic 2 provides a set of components to display lists. We don’t cover all but we can have a great UI by combining the ion-list, ion-item and ion-thumbnail components.

We iterate over the books property to create corresponding ion-item elements.

<ion-content>
  <ion-list>
    <ion-item *ngFor="#book of books">
      <ion-thumbnail item-left>
        <img [src]="book.image">
      </ion-thumbnail>
      <h2>{{book.title}}</h2>
      <p>
        <template ngFor #author #last="last" [ngForOf]="book.authors">
          {{author.name}}<template [ngIf]="!last">,</template>
        </template> • {{book.publishingYear}}
      </p>
      <button clear item-right (click)="openNavBookDetailsPage(book)">View</button>
    </ion-item>
  </ion-list>
</ion-content>

You can notice the use of the de-sugared expressions for ngFor and ngIf. Their usage prevents from adding unnecessary tags in the authors description.

Here is the result:

image04

Since the list of items is loaded asynchronously using a HTTP call, we add a spinner to notify the user that the request is in progress.

<ion-content>
  <ion-list>
    <ion-item *ngIf="loading">
      <ion-spinner name="crescent"></ion-spinner>
      </ion-item>
      <ion-item *ngFor="#item of items">
        (...)
      </ion-item>
  </ion-list>
</ion-content>

To center the spinner, we need to add the following CSS code:

ion-spinner {
  display: block;
  margin: auto;
}

Handling lists

Because space is expensive in mobile UIs, using buttons for such operations isn’t the right approach. Leveraging specific gestures is a much better approach and is commonly used for such kind of application:

  • Refreshing the list. Most of mobile applications implement the “Pull down” action. When pulling down the list, its elements are refreshed.
  • Pagination. At this level, the “infinite scroll” can be interesting. When the end of the list is reached, the application executes another HTTP call to load the next data bucket.

Pulling down the list

This gesture is common in mobile applications like the Yahoo or Gmail ones. To refresh a list (of emails for example), we can pull down the list and the application will automatically trigger a refresh of the list.

Ionic 2 supports this feature natively through its ion-refresher component.

<ion-content>
  <ion-list>
    (...)
  </ion-list>

  <ion-refresher (refresh)="doRefresh($event)">
  </ion-refresher>
</ion-content>

Here is the result:

image05 image08

We can also customize the behavior and appearance of this refresher. Additional events are supported like when the refresher start or on pulling. We can also customize the pulling icon.

Here is a sample:

<ion-content>
  <ion-list>
    (...)
  </ion-list>

  <ion-refresher (start)="doStarting()"
             (refresh)="doRefresh($event)"
             (pulling)="doPulling($event)">               
    <ion-refresher-content>
      <pullingIcon="arrow-down"
           pullingText="Pull to refresh"
           refreshingSpinner="circles"
           refreshingText="Refreshing...">
    </ion-refresher-content>                    
  </ion-refresher>
</ion-content>

The last gesture that we will describe is the infinite scroll.

Implementing infinite scroll

For lists with potentially a lot of items, we can load all the data at the beginning. In mobile applications, we rather choose to load data on demand when the user scrolls the list.

Ionic 2 provides a support for this with its ion-infinite-scroll component. It automatically adds this behavior when adding it into an ion-content block, as described below. To trigger the load of the next set of data, we need to register a method on the provided infinite event. In our case, it corresponds to the doInfinite method of the component.

<ion-content>
  <ion-list>
    (...)
  </ion-list>

  <ion-infinite-scroll (infinite)="doInfinite($event)">
    <ion-infinite-scroll-content>
    </ion-infinite-scroll-content>
  </ion-infinite-scroll>
</ion-content>

The doInfinite method accepts an object to complete the operation when a new dataset is loaded. In our case, this method call again the loadBooks method and trigger the infinite scroll when the result is there.

doInfinite(infiniteScroll) {
  this.loadBooks()
    .subscribe(payload => {
      this.books = this.books.concat(payload.list);
      infiniteScroll.complete();
    });
}

We need to update the loadBooks method to support the loading of data pages. APISpark supports the $page and $size query parameters respectively for the page number and the number of elements in a page.

First initialize the number of the last retrieved page in the constructor.

constructor(http) {
  (...)
  this.page = 0;
  (...)
}

We can then leverage this property in the loadBooks method to add the corresponding query parameters to our request.

loadBooks() {
  this.page++;

  var search = new URLSearchParams();
  if (this.page > 1) {
    search.set('$page', this.page);
  }
  search.set('$size', 8);

  return this.http.get('https://bookapi.apispark.net/v1/books/', { search })
    .map(res => res.json())
    .catch(err => {
      return Observable.of({ list: [] });
    });
}

Here is the result in action.

image06 image02

Conclusion

As we can see, Ionic 2 has made an effort to provide a set of components and tools to make simpler the implementation of mobile applications. The framework allows us to easily provide modern UX for such applications.

We described in this article how to implement a side menu and tabs but also ways to handle lists like pull down and infinite scroll. All of them rely on gestures like swipe and pull down and make applications easier to use for end users.

We saw how Ionic 2 leverages Angular 2 components and mechanisms to implement such applications. This contribute to make thing simple and provide a good organization of code.