SQLite storage in Ionic 2

In my last blog I promised to provide step by step guide to implement on phone storage. The main purpose of storing data on phone (offline support) is to reduce load time and optmize the downloads.

In this blog I am sharing my experience on how to use storage with Ionic 2.

There are different ways of integrating storage with Ionic 2 app. I was considering two options, 1) use local-storage which is collection of key-value pairs within application or 2) use SQLite database. I chose SQLite database. The reason being,  local storage has key value structure, its hard to query large amount of data, plus it has limitation of 10 MB data.

There are few other blogs, but I found them little convoluted. So, I tried to simplify steps. Let’s see, step by step procedure.

Step 1: Basic Installation

Install latest version of Ionic and Cordova by –

       npm install -g ionic
       npm install -g cordova
     

Step 2: Start Project

Lets start new project with

     ionic start SampleProject blank --v2
   

Here by specifying v2 in command, I am referring it as Ionic 2 project. Also SampleProject will be created with blank theme.

Now add platform to project. Basically here I am just focusing on android platform. If you want it for IOS too, then you can add it with same command but replace android with IOS.

     ionic platform add android
   

Step 3: Add SQLite plugin

Next step is to add SQLite plugin. This plugin is provided by Cordova SQLite Storage. After installing add package name in package.json file. I am adding it with following command :

    ionic plugin add cordova-sqlite-plugin
  

Step 4: Add database provider

There are many ways, In which you can manage various operation over a SQLite database. Here is my plan, I am creating one provider which will be called as ‘Database Provider’. In that I will be specifying all CRUD operation including database open and destroy function. After that I will access that provider wherever I want. So, lets do it.

Add provider with,

     ionic g provider DatabaseProvider
   

Add following content in database-provider.ts file :

     import { Injectable } from '@angular/core';
     import { Http } from '@angular/http';
     import { SQLite } from 'ionic-native';

     @Injectable()
     export class DatabaseProvider {
       db = new SQLite();

       constructor(public http: Http) {
       }

       public openDatabase() {
         return this.db.openDatabase({name: 'user_data.db', location: 'default'});
       }

       public createUserTable(){
         let query = "CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT)";
         this.db.executeSql(query, {}).then(() => {
           console.log("user table created");
         });
       }

       public insertIntoUser(user_details){
         let insert_query = "INSERT INTO user VALUES (?, ?)";
         this.db.executeSql(insert_query, [user_details.first_name, user_details.last_name]).then((data) => {
           console.log("user inserted");
         });
       }

       public fetchFromUser(){
         return this.db.executeSql('SELECT * FROM user', []);
       }

      public dropDatabase(){
        console.log("dropping database");
        SQLite.deleteDatabase({name: 'user_data.db', location: 'default'});
      }
    }
   

Here I am first importing SQLite native class provided by Ionic CLI and then making its instance. While opening database we should provide database name and location to store it. Same parameters will be passed while dropping database.

Step 5: Open database in app.component.ts file

Now its time to open database, I want to open database when my app is started, so I will do it in app.component file as,

    import { Component } from '@angular/core';
    import { Platform } from 'ionic-angular';

    import { HomePage } from '../pages/home/home';
    import { DatabaseProvider } from '../providers/database-provider';

    @Component({
      templateUrl: 'app.html'
    })
    export class MyApp {
      rootPage = HomePage;

      constructor(platform: Platform, public dbService: DatabaseProvider) {
        platform.ready().then(() => {
          Promise.all([this.dbService.openDatabase()]).then((data) => {
            console.log("database is open now");
          });
        });
      }
    }
  

Here I am just importing database provider, which we have already created. Once platform gets ready I am opening it by just calling openDatabase() method.

Step 6: Apply operations on database

Now I am going to do all insert and fetch operation on database in home page. So now, I am again importing database-provider in home page. After that I will create table USER, add data to it and fetch user from it. In this example I am adding static data. In real-time we could store data coming from server side in SQLite DB. So here is the magic,

     import { Component } from '@angular/core';
     import { NavController } from 'ionic-angular';

     import { DatabaseProvider } from '../../providers/database-provider';

     @Component({
       selector: 'page-home',
       templateUrl: 'home.html'
     })
     export class HomePage {
       first_name:any = 'Kevin';
       last_name:any = 'Watson';
       user_list:any;
       constructor(public navCtrl: NavController, public dbService: DatabaseProvider) {
       }

       insert(){
         this.dbService.insertIntoUser({first_name: this.first_name, last_name: this.last_name});
       }

       refresh(){
         Promise.all([this.dbService.fetchFromUser()]).then((data) => {
           this.user_list = data;
         });
       }
    }
   

Now update the code in home.html file. Here I am showing two buttons and list of users. Add button will add record ( first name and last name ) in User table. Refresh button will take updated data from User table and store it to user_list. We can do any opration on DB. Just write that method in provider and access it in component.

     <ion-header>
       <ion-navbar>
         <ion-title>
           User List
         </ion-title>
         <ion-buttons start>
           <button (click)="refresh()">Refresh</button>
         </ion-buttons>
         <ion-buttons end>
           <button (click)="insert()">Add</button>
         </ion-buttons>
       </ion-navbar>
     </ion-header>

     <ion-content padding>
       <ion-list>
          <ion-item *ngFor="let user of user_list">
             {{user.firstname}} {{user.lastname}}
          </ion-item>
       </ion-list>
     </ion-content>
  

In this example, the data we are storing is static. In real time, we fetch data from server and store it in database. Periodically we need to have watch on server db to get updated data. There are two ways, either we could use tools available for auto sync of a database or on some action we will manually check for update.

Tools like PouchDB can be used with SQLite. It needs URL of remote database server to sync with. Second way is to check for the updated data on some action/ event. If data is updated then we will update our local data with same.

In this way, We can store data in application and also retrieve it whenever needed. For example we could load data from local DB when we have no internet connection. This will serves our purpose of offline support.

Benefits of Ionic 2

Ionic is world’s most popular cross platform mobile development technology. Basically its hybrid mobile app development framework. Build on top of Cordova, which enables us to build app along with web technologies. The goal behind developing Ionic is to give web developers a way to use their skill-set to build mobile applications.

Recently Ionic 2 has been launched. Conceptually Ionic 2 is similar to Ionic 1. In Ionic 2 controller hold all logical part and view is handled by template, except controllers are classes. Like this there are many key differences. Ionic 2 is build on the base of Angular 2 which uses typescript. All controllers are written in .ts file. Hence while building a app, these files get converted into .js files. This is the process of ‘Transpiling’. Transpiling does not allow logic to be directly available through debugging.

Now a days Ionic 2 is top choice for fast development. There are some reasons why it is more preferable than Ionic 1. Followings are some key points why we should migrate from Ionic 1 Ionic 2 :

Speed :

In Angular 2 change detection in model is very quick. So, Ionic 2 app is faster than  Ionic 1.

Organized directory structure :

In Ionic 1, there is no standard way to organize our files. User can arrange file as per his/ her convenience. But Ionic 2 comes with its own directory structure. Every page is referred as component. Each component comes with its own folder. It contains .ts file as class/ controller, .css file and .html file as a view. For e.g. suppose we have two pages as home and about. Then structure be like :

Ionic 1 :

  • /www
    • /js
      • home.js
      • about.js
    • /templates
      • home.html
      • about.html
    • /css
      • home.css
      • about.css

Ionic 2 :

  • Home
    • home.ts
    • home.html
    • home.css
  • About
    • about.ts
    • about.html
    • about.css

Navigation :

In Ionic 1, navigation is done by routing through states. But in Ionic 2, more easier approach is used that is ‘stack’. When we want to redirect to particular page, we will just push that page onto navigation stack. We can come back to previous page by popping the current page from stack. As simple as that. Lets see difference between Ionic 1 and Ionic 2 navigation :

Ionic 1 : We can specify routes like this,

  $stateProvider
   .state('home', {
     url: '/home',
     templteUrl: 'templates/home/index.html',
     controller: 'HomeController'
   })
   .state('about', {
     url: '/about',
     templateUrl: 'templates/about/index.html',
     controller: 'AboutController'
   });

Whenever we want to go from home state to about state we just give,

  $state.go('about');

Ionic 2 : Similarly, suppose we have home page as,

  import { Component } from '@angular/core';
  import { NavController } from 'ionic-angular';
  import { About } from '../about/about';

  @Component({
    selector: 'page-home',
    templateUrl: 'home.html'
  })
  export class Home {
    constructor(public navCtrl: NavController){}
    loadAbout(){
     // load about page
    }
  }

To go to about page, we just push page onto navigation stack,

  loadAbout(){
    this.navCtrl.push(About);
  }

To remove page from stack or to go to previous page,

  this.navCtrl.pop();

Generators :

In Ionic 1, whenever we need to add new controller, we just define it in some file. Then associate controller with relative template file with help of state-provider. This process is very simple in Ionic 2. With Ionic 2 CLI, we can easily generate pages, providers, pipes, etc. Ionic 2 provides generator command for it. Lets have a look,

1. Pages :

These are main component of app. When we create new page, it comes with 3 files as mentioned above. To generate page command is :

  ionic generate page Home or ionic g page Home

The page comes with default structure. Its looks like :

  import { Component } from '@angular/core';
  import { NavController } from 'ionic-angular';

  @Component({
    selector: 'page-home',
    templateUrl: 'home.html'
  })
  export class HomePage {

    constructor(public navCtrl: NavController) {}

    ionViewDidLoad() {
      console.log('Hello HomePage Page');
    }

  }

2. Provider:

Providers are similar to service in Ionic 1. Every provider is injectable. Means we can import or inject service, provided by provider. The command to generate providers is :

  ionic g provider ApiServiceProvider

The default structure of provider is :

  import { Injectable } from '@angular/core';
  import { Http } from '@angular/http';
  import 'rxjs/add/operator/map';

  @Injectable()
  export class ApiServiceProvider {

    constructor(public http: Http) {
      console.log('Hello ApiServiceProvider Provider');
    }

  }

3. Pipe:

Pipe is one of the decorator. The main goal of pipe is to transform the given set of values. Pipe is also injectable. The default method in each pipe class is ‘transform’. Our input transformation logic comes here. The command to generate Pipe :

  ionic g pipe Lowercase

The default structure of pipe is :

  import { Injectable, Pipe } from '@angular/core';

  @Pipe({
    name: 'lower'
  })
  @Injectable()
  export class LowerCase {
    transform(value, args) {
      value = value + ''; // make sure it's a string
      return value.toLowerCase();
    }
  }

We can use this pipe in template as,

  <label>{{name | lower}}</label>

Set of API :

To build dynamic app, we may need various cordova plugins. In Ionic 1, when we use any plugin we need to inject related cordova service in relavant controller. Then only we can access plugin feature. For example, suppose we want to use cordova plugin to access contact from mobile. For it we will install plugin ‘cordova-plugin-contacts’. Once done, we can use it like this in Ionic 1:

  angular.module("app")
    .controller('ContactController', function($scope, $cordovaContacts, $cordovaToast){
      $scope.findContact = function(){
        Contacts.find(['*'], {filter  : "TPN whatsapp number"}).then(function(contactFound){
           if(contactFound.length >= 1){
             $cordovaToast.showShortBottom('Contact Found!!!');
           }
        });
      }
    })

This scenario looks clean in Ionic 2. It provides large set of Native API classes. Using these classes, we can easily use any  plugin with app. To integrate contact plugin, Ionic 2 provides native class names as ‘Contacts’. We can use it like this:

  import { Component } from '@angular/core';
  import { ToastController } from 'ionic-angular';
  import { Contacts } from 'ionic-native';

  @Component({
    selector: 'page-home',
    templateUrl: 'home.html'
  })
  export class Home {
    constructor(public toastCtrl: ToastController){}
    findContact(){
      Contacts.find(['*'], {filter  : "Support Contact Number"}).then((contactFound) => {
        if(contactFound.length >= 1){
          let toast = this.toastCtrl.create({
            message: "Contact Found!!!",
            duration: 2000,
            position: 'bottom'
          });
          toast.present();
        }
      });
    }
  }

Support for cross-platform :

Ionic 1 comes with support to only 2 platforms, i.e. Android and IOS. Ionic 2 allows us to build app which is compatible with Android, IOS as well as Windows phone.

In next blog we will see what major issue we can face while developing an app in Ionic 2 and solution to it.