Mobile
Article

Synchronizing Offline App Data with PouchDB

By Jay Raj

Applications which can work online and offline are an advantage to mobile users. For example, someone traveling by air generally has no internet connection. With a well designed app they can still work in offline mode and synchronize the offline data with an application server.

How Does This Work?

When the application is offline, the data is stored using HTML 5 local storage and session storage objects to store data in the browser or WebView. When the application is online, its programming logic will synchronize changes to a central database.

PouchDb

PouchDb is an open source JavaScript database inspired by CouchDb which helps developers build applications that work in offline and online modes.

What We’;Ll Build

We’ll be using the IONIC framework to create an expense logger mobile application making use of PouchDB for the back end.

The source code for this tutorial is available on GitHub.

Getting Started

Start by installing IONIC using the node package manager (npm).

npm install -g cordova ionic

Once IONIC has installed, create a blank project called iPouch.

ionic start iPouch blank

Navigate to the project directory and install the required dependencies.

cd iPouch
npm install

Run the following command in the terminal to start the app in the web browser

ionic serve

Download and add the PouchDB JavaScript to the iPouch/www/js folder.

Add a reference to the pouchdb-4.0.x.min.js script in the iPouchDb/www/index.html page.

<script src="js/pouchdb-4.0.0.min.js"></script>

(replacing x with the current version number)

Setting up CouchDb

Use of your platforms package manager of choice to install CouchDb.

sudo apt-get install couchdb // For Ubuntu
brew install couchdb  //  For MAC

And start couchdb with the couchdb command.

To allow cross domain requests from PouchDb to CouchDb we need to install add-cors-to-couchdb.

npm install -g add-cors-to-couchdb

Once installed enable the CORS by running the following command:

add-cors-to-couchdb

Couch Db should be running at port 5984. Try browsing http://localhost:5984/ .

Adding the PouchDb Service

Let’s start by creating a controller for our application. Add a file called controller.js to the iPouch/js folder. Add a reference to the controller in index.html:

<script type="text/javascript" src="js/controller.js"></script>

Inside controller.js define a module called starter.controllers.

angular.module('starter.controllers', ['ionic'])

Define a controller called HomeCtrl.

.controller('HomeCtrl', ['$scope', function($scope) {

}])

Create an AngularJS factory service which will return the PouchDb object.

.factory('pouchdb', function() {
  return new PouchDB('myApp');
});

Inject the pouchdb service into HomeCtrl and create a new pouch database for both the local pouch db and the remote couch db.

.controller('HomeCtrl', ['$scope','pouchdb', function($scope,pouchdb) {

    var dbLocal = new PouchDB('dbname');

    var dbRemote = new PouchDB('http://localhost:5984/dbname');
}])

Here is the complete controller.js file so far.

angular.module('starter.controllers', ['ionic'])

.controller('HomeCtrl', ['$scope','pouchdb', function($scope,pouchdb) {

    var dbLocal = new PouchDB('dbname');

    var dbRemote = new PouchDB('http://localhost:5984/dbname');
}])

.factory('pouchdb', function() {
  return new PouchDB('myApp');
});

Add the starter.controllers module to application by injecting the module in app.js.

angular.module('starter', ['ionic','starter.controllers'])

Add Expense Tracking Functionality

Let’s start creating a user interface for adding expense items and amounts. Open www/index.html and change the existing body content as shown below :

<body ng-app="starter" ng-controller="HomeCtrl">
    <ion-pane>

        <ion-header-bar class="bar-stable">
            <h1 class="title">Expense Checker</h1>
            <button class="button" ng-click="openModal()">Add</button>
        </ion-header-bar>

    </ion-pane>
</body>

Here we created a home page with the header button on the right side of the screen. We added a click event on the button which will open a modal window. Save the changes and you should be able to view the home screen.

Basic user interface complete

We’ll use IONIC modals to show the modal pop up. Add the following template code for a modal pop up to the index.html page after the ion-pane ending tag.

<script id="my-modal.html" type="text/ng-template">

    <ion-modal-view>
        <ion-header-bar>
            <h1 class="title">Add Expense</h1>
        </ion-header-bar>
        <ion-content>


            <div class="list">
                <label class="item item-input">
                    <input type="text" ng-model="item.expense" placeholder="Expense">
                </label>
                <label class="item item-input">
                    <input type="number" ng-model="item.amount" placeholder="Amount">
                </label>
                <button ng-click="add()" class="button button-full button-positive">
                    Add Expense
                </button>

            </div>
        </ion-content>
    </ion-modal-view>
</script>

To show the modal, inject $ionicModal into the controller.

.controller('HomeCtrl',['$scope','pouchdb','$ionicModal',function($scope,pouchdb,$ionicModal)

Initialize the modal pop inside the HomeCtrl controller.

$ionicModal.fromTemplateUrl('my-modal.html', {
    scope: $scope,
    animation: 'slide-in-up'
}).then(function(modal) {
    $scope.modal = modal;
});

Add two functions inside the HomeCtrl to show and close the modal pop up.

$scope.openModal = function() {
    $scope.modal.show();
};

$scope.closeModal = function() {
    $scope.modal.hide();
};

$scope.$on('$destroy', function() {
        $scope.modal.remove();
});

In the above code I have an extra destroy event call to clean up the modal pop up. Here is how the pop up looks:

Modal pop-up

Define the add function to add the data to pouchdb.

$scope.add = function(){
    // Code would be here !!
};

We’ll make use of the db.put() API to create a new document in pouchdb. Start by creating an item object to save.

var timeStamp = String(new Date().getTime());

var item = {
        "_id": timeStamp,
        "expense": $scope.item.expense,
        "amount": $scope.item.amount
};

Once we have the item object, we’ll post it to pouchdb. On success we’ll add the item to the items array which will later populate the display.

dbLocal.put(
    item
).then(function (response) {
    $scope.items.push(item);   // Add to items array
    $scope.closeModal();      // Close the modal
}).catch(function (err) {
    console.log(err);
});

Once the data is added we’ll use the db.allDocs API to fetch the documents added to pouchdb. Inside the Home controller add the following code to get all documents from pouchdb.

dbLocal.allDocs({
    include_docs: true
}).then(function(result) {
   console.log(result)
}).catch(function(err) {
    console.log(err);
});

We’ll iterate through the results and push item objects into an items array. Define an items array in the Home controller.

$scope.item = {};
$scope.items = [];

Iterate through the results received, creating and inserting an item object into the array list. Update dbLocal.allDocs to the following:

dbLocal.allDocs({
    include_docs: true
}).then(function (result) {
    console.log('re    var dbLocal = new s is',result.rows);
    for(var i=0;i<result.rows.length;i++){
        var obj = {
            "_id": result.rows[i].doc.id,
            "expense": result.rows[i].doc.expense,
            "amount": result.rows[i].doc.amount
        }
        $scope.items.push(obj);
        $scope.$apply();
    }
    console.log($scope.items);
}).catch(function (err) {
    console.log(err);
});

$scope.$apply() is to update the bindings. To display the content of items we’ll use the AngularJS ngRepeat directive. In index.html add the following code to display ul element.

 <ion-view title="iPouch">
      <ion-content>
        <ul class="list">
            <li ng-repeat="i in items" class="item">
              Amount spent on {{i.expense}} is {{i.amount}}
            </li>
        </ul>
      </ion-content>
</ion-view>

Save the above changes and try adding a new expense using the application. Once added you should be able to view the data on the screen as shown:

Expense Listings

Sync Data When Offline

As you would have noticed we have defined two database in the Home controller, dbLocal and dbRemote. We save the data in the dbLocal database which is the pouchdb database. Once we have the data in the local database we can replicate the data to a remote server. This way when the application is offline the data is saved in the local database and when online, replicated to the remote server.

We’ll make use of the replicate API to replicate data to the remote when online. Add the following line of code to replicate data to remote.

dbLocal.replicate.to(dbRemote,{live:true},function(err){
        console.log(err);
});

We have specified the live : true option to allow live replication. Save the above changes and add a new item. Items replicated to the remote couch database are visible using the remote url in browser.

http://localhost:5984/dbname

Conclusion

In this tutorial, we learned how to create an IONIC application with a pouchdb back end. We saw how to store data locally when offline and replicate it to the server when online.

Read the pouchdb documentation to get a detailed insight about more of the features available to your application.

Let me know your thoughts, suggestions or corrections in the comments below.

  • hot_rush

    oh god, this db is opened for everybody) any secure options, authorization? but think without api proxying it is unsecure anyway

  • Yimiprod

    I don’t get why set a third db in a factory and never use it

  • http://rubyrock.me/ Pierre-Alexandre Piarulli

    It look awesome ! The only one question i have is how to deal with auth in pouchd, can we mix it with other auth system like fb or tweeter. How can we connect pushdb with some other backend framework like ruby on rails.

    Thanks for this well documented post guy !

  • Jaden Ng

    What is the difference if using firebase instead of pouchd ?

    • Andrew Radulescu

      Trying to read your mind: Firebase doesn’t provide full offline capabilities for Javascript yet and we’re looking for something out of the box.

  • Michael Vu

    How do you sync from remote db to local db? In case there are more than 1 user, we have to do 2-way sync between remote and local db, right?

    • Michael Vu

      I found that they have bi-sync. That’s great!

  • Surya Teja

    It looks great.But my question is pouchdb can store max upto 50mb .What if the data exceeds 50mb. Is there any way to delete data in pouchdb after synchronizing with remote database?

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Mobile, once a week, for free.