home ionic 1 tutorials ionic 2 tutorials

18
5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2 http://gonehybrid.com/howtousepouchdbsqliteforlocalstorageinionic2/ 1/18 HOME IONIC 1 TUTORIALS IONIC 2 TUTORIALS How To Use PouchDB + SQLite For Local Storage In Ionic 2 18 APRIL 2016 on Local Storage, Ionic 2, TypeScript, Angular 2, PouchDB, SQLite | Comments A year ago I wrote a tutorial on how to use PouchDB + SQLite for an Ionic 1 app. Now that Ionic 2 is in beta, I've updated the tutorial for Ionic 2 and the recently released Cordova SQLite Plugin 2. The source code can be found on GitHub. What is PouchDB? PouchDB is an open-source JavaScript library that uses IndexedDB or WebSQL to store data in the browser. It's inspired by Apache CouchDB and allows you to sync your local data with a CouchDB server. What I like about PouchDB is that it uses a NoSQL approach to database storage, which greatly simpli¿es the code you 21 Shares 8 4 Get updates for new Ionic 2 tutorials. SIGN UP HERE

Upload: independent

Post on 04-Dec-2023

0 views

Category:

Documents


0 download

TRANSCRIPT

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 1/18

HOME IONIC 1 TUTORIALSIONIC 2 TUTORIALS

How To Use PouchDB + SQLiteFor Local Storage In Ionic 218 APRIL 2016 on Local Storage, Ionic 2, TypeScript, Angular 2, PouchDB,SQLite | Comments

A year ago I wrote a tutorial on how to use PouchDB +

SQLite for an Ionic 1 app. Now that Ionic 2 is in beta, I've

updated the tutorial for Ionic 2 and the recently released

Cordova SQLite Plugin 2.

The source code can be found on GitHub.

What is PouchDB?PouchDB is an open-source JavaScript library that uses

IndexedDB or WebSQL to store data in the browser. It's

inspired by Apache CouchDB and allows you to sync your

local data with a CouchDB server.

What I like about PouchDB is that it uses a NoSQL approach

to database storage, which greatly simpli¿es the code you

21Shares

8

4

Get updates for new Ionic 2 tutorials.  SIGN UP HERE

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 2/18

need to write. And then there is the out-of-the-box

syncing with a server, but in this tutorial we'll only focus

on local storage.

There are storage limits for IndexedDB and WebSQL

databases, so if you want unlimited and reliable storage on

a mobile device, you're better o¼ using SQLite. PouchDB

will automatically use SQLite if you have installed a

Cordova plugin for it and have con¿gured it to use a

WebSQL adapter.

Note: SQLite is slower than IndexedDB/WebSQL as mentioned in

this article by Nolan Lawson.

Set up the librariesLet's start by creating our Ionic 2 app.

$ ionic start ionic2‐tutorial‐pouchdb blank ‐‐v2 ‐‐ts $ cd ionic2‐tutorial‐pouchdb 

We'll have to install a couple of libraries into our app to get

PouchDB working with SQLite.

SQLite Plugin 2 for Cordova

PouchDB library

To install SQLite Plugin 2 execute the following command

in your Ionic app directory:

$ ionic plugin add cordova‐plugin‐sqlite‐2 

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 3/18

Next, we'll install PouchDB.

$ npm install pouchdb ‐‐save 

Because this tutorial is written in TypeScript, it would be

nice to have type de¿nitions for the PouchDB library, but

the ones on De¿nitelyTyped are over 2 years old, so let's

skip that.

If we want to import the PouchDB library without the help

of type de¿nitions, we need to use require() , so let's install

the type de¿nition for it:

$ typings install require ‐‐ambient ‐‐save 

Tip: If you want to know more about using external libraries in

Ionic 2, check out this blog post by Mike Hartington.

We are done with setting up the necessary libraries, you

now have everything you need to start writing code!

What are we going to build?Our app is going to be a birthday registration app that will

have add, update, delete and read functionality.

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 4/18

Create database serviceLet's go ahead and create a service to encapsulate our

PouchDB calls in app/services/birthday.service.ts.

import {Injectable} from 'angular2/core';  let PouchDB = require('pouchdb');  @Injectable() export class BirthdayService {       private _db;     private _birthdays;      initDB() {         this._db = new PouchDB('birthday2', { adapter: 'websql'    } 

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 5/18

We need to initialize the database, if it doesn't exist, a new

database will be created.

As you can see we're setting the adapter to WebSQL, the

way PouchDB works is that if you have the SQLite plugin

installed, it will automatically use that, otherwise it will fall

back to WebSQL.

Add a birthdayLet's write the code for adding a birthday to our database.

add(birthday) {       return this._db.post(birthday); }    

Is that all? Yes, that is all you have to do!

We don't need to write a SQL INSERT statement and map the

data to a SQL table. In PouchDB the birthday object is

simply serialized into JSON and stored in the database.

There are 2 ways to insert data, the post method and the

put method. The di¼erence is that if you add something

with the post method, PouchDB will generate an _id for

you, whereas if you use the put method you're generating

the _id yourself.

Update a birthday

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 6/18

Update a birthday

update(birthday) {       return this._db.put(birthday); } 

Delete a birthday

delete(birthday) {       return this._db.remove(birthday); } 

Get all birthdaysLet's get all the birthdays saved in the database.

getAll() {        if (!this._birthdays) {         return this._db.allDocs({ include_docs: true})             .then(docs => {                  // Each row has a .doc object and we just want to send an                 // array of birthday objects back to the calling controller,                // so let's map the array to contain just the .doc objects.                 this._birthdays = docs.rows.map(row => {                     // Dates are not automatically converted from a string.                    row.doc.Date = new Date(row.doc.Date);                     return row.doc;                 }); 

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 7/18

                 // Listen for changes on the database.                 this._db.changes({ live: true, since: 'now'                    .on('change', this.onDatabaseChange);                  return this._birthdays;             });     } else {         // Return cached data as a promise         return Promise.resolve(this._birthdays);     } } 

We use the allDocs function to get an array back of all the

birthday objects in the database. I don't want the code that

will be calling this service to know anything about docs or

PouchDB, so I've mapped the rows array to a new array that

only contains the row.doc objects.

As you can see there is also a conversion of the row.doc.Date

property to an actual Date , because unfortunately, the

dates in JSON will not be automatically converted back to

Date objects.

I also save the output in the _birthdays array so the data

will be cached and I will only have to get the data from the

database one time on start of the app.

"But...", you ask, "how will I keep that cached data in sync with

the database when there is data added or changed?"

Well, I'm glad you asked, that's where the onDatabaseChange

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 8/18

function comes in.

private onDatabaseChange = (change) => {       var index = this.findIndex(this._birthdays, change.id);    var birthday = this._birthdays[index];      if (change.deleted) {         if (birthday) {             this._birthdays.splice(index, 1); // delete         }     } else {         change.doc.Date = new Date(change.doc.Date);         if (birthday && birthday._id === change.id) {             this._birthdays[index] = change.doc; // update         } else {             this._birthdays.splice(index, 0, change.doc) // insert        }     } }  // Binary search, the array is by default sorted by _id. private findIndex(array, id) {       var low = 0, high = array.length, mid;     while (low < high) {     mid = (low + high) >>> 1;     array[mid]._id < id ? low = mid + 1 : high = mid     }     return low; } 

Inspired by this post: E覹䶘ciently managing UI state with

PouchDB.

This function allows you to update the _birthdays array

whenever there is a change on your database. The input for

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 9/18

this method is a change object that contains an id and the

actual data in a doc object. If this id is not found in the

_birthdays array it means that it is a new birthday and we

will add it to the array, otherwise it's either an update or a

delete and we make our changes to the array accordingly.

Let's build the UIOK, so we have the service set up which does most of the

heavy work, let's have a look at the UI.

We'll create 2 pages for our app, one to display the list of

birthdays (HomePage) and one to add or edit a birthday

(DetailsPage).

Before we do the implementation of the pages, we need to

set up our service as a provider. We can do that per page

but that means we will get a new instance of the service per

page and we won't be able to use the cached data.

So, let's add the service at the parent level, which in this

case is app.ts.

import {BirthdayService} from './services/birthday.service' @App({   template: '<ion‐nav [root]="rootPage"></ion‐nav>',   config: {},    providers: [BirthdayService] }) 

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 10/18

Now we'll get a shared instance of BirthdayService in our

HomePage and DetailsPage .

HomePageWe'll implement the template home.html ¿rst, which uses

an <ion‐list> to display all the birthdays.

<ion‐navbar *navbar>     <ion‐title>        Birthdays      </ion‐title>   <ion‐buttons end>       <button (click)="showDetail()">           <ion‐icon name="add"></ion‐icon>       </button>   </ion‐buttons> </ion‐navbar>  <ion‐content class="home">         <ion‐list inset>           <ion‐item *ngFor="#birthday of birthdays" (click)="showDetail(birthday)">            <div item‐left>{{ birthday.Name }}</div>             <div item‐right>{{ birthday.Date | date:'yMMMMd' }}          </ion‐item>        </ion‐list> </ion‐content>   

Angular 2 uses the Internationalization API to do date

formatting, which is pretty cool, but doesn't work in Safari.

So you have 2 options, you can either use this poly¿ll or

write your own date formatting pipes. For this tutorial we'll

use the poly¿ll, which means that you need to add this line

to your index.html.

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 11/18

<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features

Now it's time to open up home.ts and write the code to get

the data from the service.

We'll wait for the platform.ready() event to ¿re before we

try to access the database. We then call

birthdayService.getAll() and because it's an asynchronous

call, we need to use zone.run() to let Angular know that it

needs to do change detection and update the view.

Tip: Watch the talk Angular 2 Change Detection Explained by

Pascal Precht.

import {Page, Modal, NavController, Platform} from 'ionic‐angular'import {BirthdayService} from '../../services/birthday.service'import {DetailsPage} from '../details/details';   import {NgZone} from 'angular2/core';  @Page({     templateUrl: 'build/pages/home/home.html' }) export class HomePage {       public birthdays = [];      constructor(private birthdayService: BirthdayService,         private nav: NavController,         private platform: Platform,         private zone: NgZone) {      }      onPageLoaded() { 

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 12/18

        this.platform.ready().then(() => {             this.birthdayService.initDB();              this.birthdayService.getAll()                 .then(data => {                     this.zone.run(() => {                         this.birthdays = data;                     });                 })                 .catch(console.error.bind(console));         });     }      showDetail(birthday) {         let modal = Modal.create(DetailsPage, { birthday: birthday         this.nav.present(modal);          modal.onDismiss(() => {          });     } } 

DetailsPageAdd a new page with this command:

$ ionic g page details ‐‐ts 

Add the following code in details.html. For now I'm using a

plain text input type for the date, in the next beta release of

Ionic 2 there should be support for date pickers.

<ion‐navbar *navbar>     <ion‐title>{{ action }} Birthday</ion‐title> 

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 13/18

  <ion‐buttons end *ngIf="!isNew">       <button (click)="delete()">           <ion‐icon name="trash"></ion‐icon>       </button>   </ion‐buttons> </ion‐navbar>  <ion‐content padding class="details">       <ion‐list inset>         <ion‐item>             <ion‐label>Name</ion‐label>             <ion‐input type="text" [(ngModel)]="birthday.Name        </ion‐item>         <ion‐item>             <ion‐label>Birthday</ion‐label>             <ion‐input type="text" [(ngModel)]="isoDate" placeholder        </ion‐item>     </ion‐list>     <button block (click)="save()">Save</button> </ion‐content>   

Add the following code in details.ts.

import {Modal, Page, NavParams, ViewController} from 'ionic‐angular'import {BirthdayService} from '../../services/birthday.service' @Page({     templateUrl: 'build/pages/details/details.html' }) export class DetailsPage {       public birthday;     public isNew = true;     public action = 'Add';     public isoDate = '';      constructor(private viewCtrl: ViewController,         private navParams: NavParams, 

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 14/18

        private birthdayService: BirthdayService) {     }      onPageLoaded() {         this.birthday = this.navParams.get('birthday');          if (!this.birthday) {             this.birthday = {};         }         else {             this.isNew = false;             this.action = 'Edit';             this.isoDate = this.birthday.Date.toISOString()        }     }      save() {         this.birthday.Date = new Date(this.isoDate);          if (this.isNew) {             this.birthdayService.add(this.birthday)                 .catch(console.error.bind(console));         } else {             this.birthdayService.update(this.birthday)                 .catch(console.error.bind(console));         }          this.dismiss();     }      delete() {         this.birthdayService.delete(this.birthday)             .catch(console.error.bind(console));          this.dismiss();     }      dismiss() {         this.viewCtrl.dismiss(this.birthday);     } 

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 15/18

We're Done!You can now test the app in the browser (where it will use

WebSQL).

$ ionic serve 

And on your iOS and Android devices (where it will use

SQLite).

$ ionic run ios $ ionic run android 

Inspecting the databaseThere is a Chrome extension called PouchDB Inspector that

allows you to view the contents of the database in the

Chrome Developer Tools.

The PouchDB Inspector only works for IndexedDB

databases and you'll need to expose PouchDB as a property

on the window object for it to work. Add this line of code to

the BirthdayService implementation.

window["PouchDB"] = PouchDB;   

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 16/18

You can not use the PouchDB Inspector if you loaded the app

with ionic serve ‐‐lab because it uses iframes to display the

iOS and the Androw views. The PouchDB Inspector needs to

access PouchDB via window.PouchDB and it can't access that when

the window is inside an <iframe> .

TroubleshootingKeep in mind that if you haven't speci¿ed an adapter to use

for PouchDB, it will use an IndexedDB or WebSQL adapter,

depending on which browser you use. If you'd like to know

which adapter is used by PouchDB, you can look it up:

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 17/18

var db = new PouchDB('birthdays2');   console.log(db.adapter);   

On a mobile device the adapter will be displayed as websql

even if it is using SQLite, so to con¿rm that it is actually

using SQLite you'll have to do this (see answer on

StackOverÀow):

var db = new PouchDB('birthdays2');   db.info().then(console.log.bind(console));   

This will output an object with a sqlite_plugin set to true

or false .

Delete database

var db = new PouchDB('birthdays2');   db.destroy().then(function() { console.log('ALL YOUR BASE ARE BELONG TO US'

The source code can be found on GitHub.

I hope this tutorial was helpful to you, leave a comment if

you have any questions. For more information on PouchDB

and NoSQL check out the links below.

Read More

5/30/2016 How To Use PouchDB + SQLite For Local Storage In Ionic 2

http://gonehybrid.com/how­to­use­pouchdb­sqlite­for­local­storage­in­ionic­2/ 18/18

comments powered by Disqus

Read MoreRed Pill or Blue Pill? Choosing Between SQL & NoSQL

Introduction to PouchDB

E½ciently managing UI state with PouchDB

12 pro tips for better code with PouchDB

PouchDB Blog

Syncing Data with PouchDB and Cloudant in Ionic 2

Follow me on Twitter @ashteya and sign up for myweekly emails to get new tutorials.

If you found this article useful, could you hit the share

buttons so that others can bene¿t from it, too? Thanks!

Ashteya BiharisinghI build hybrid mobile apps or read a good book

when my little boy gives me a break. Now reading:

The Art Of Learning by Josh Waitzkin.

Cork, Ireland

Share this post

Gone Hybrid © 2016 | Powered by Digital Ocean - GET $10CREDIT