未加星标

Syncing with Couchbase in an Ionic 2 App using PouchDB

字体大小 | |
[数据库(综合) 所属分类 数据库(综合) | 发布者 店小二03 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏

Not too long ago I wrote about using Couchbase in an Ionic 2 application . In the previous example we saw how to use Couchbase Lite and the RESTful API for saving data and working with Couchbase Sync Gateway. While that is the preferred approach, it isn't the only approach.

There is a great javascript library called PouchDB that can actually connect to Couchbase Sync Gateway, pulling Couchbase Lite out of the equation. This means that PouchDB can be used with Ionic 2 to synchronize data between Couchbase Server and any application that was created.

So how do we make this happen?

The Requirements

If you saw the previous tutorial, the requirements here will be no different. They are as follows:

Node.js 4.0+ Ionic Framework 2.0 Couchbase Sync Gateway Android SDK for Android and Xcode for iOS

The Ionic Framework 2.0 CLI is used for creating and building applications. It uses the Node Package Manager (NPM) found with Node.js to download all the dependencies. We won't be syncing with Couchbase Server in this example, but we'll still be using Couchbase Sync Gateway. The plan here is to make use of the in-memory option that Sync Gateway has for prototyping. Including Couchbase Server isn't difficult after this.

Preparing Couchbase Sync Gateway

Couchbase Sync Gateway handles all the orchestration of data between devices, platforms, and Couchbase Server. This includes any read and write permissions. Because of this it needs to have its own configuration based on the needs of your application.

Take the following configuration for example:

{ "log":["CRUD+", "REST+", "Changes+", "Attach+"], "databases": { "example": { "server":"walrus:", "sync":` function (doc) { channel (doc.channels); } `, "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } } } }, "CORS": { "Origin": ["http://localhost:8100"], "LoginOrigin": ["http://localhost:8100"], "Headers": ["Content-Type"], "MaxAge": 17280000 } }

The above configuration creates a database called example with no rules in terms of data channeling. This means that all devices will be able to read and write. Your needs may be different.

At the bottom of the configuration is information regarding cross origin resource sharing (CORS). The bottom bit is not necessary unless you plan to use features like ionic serve . Trying to access Sync Gateway from the same host, but different port will throw JavaScript errors. The CORS addition will prevent this. Again, this is only if you wish to serve the application in your web browser. Running the application from your device will have no issues.

Creating an Ionic Framework with PouchDB Application

The application we create will match that of what we created previously with Couchbase Lite and Ionic 2. We will be building a simple todo list application that synchronizes the items between devices.


Syncing with Couchbase in an Ionic 2 App using PouchDB

This is a single page application, but it covers all the necessary Ionic 2 and Angular 2 best practices. To create this project, execute the following from your Command Prompt (windows) or Terminal (Mac and linux):

ionic start PouchDBProject blank --v2 cd PouchDBProject ionic platform add ios ionic platform add android

The above commands will create a fresh Ionic 2 project that uses Angular 2 and TypeScript. While I've gone ahead and added both the Android and iOS build platforms, you won't be able to build for iOS unless you're using a Mac with Xcode installed.

Since this project will be using PouchDB , it needs to be installed into our project. This can be done by executing the following command:

npm install pouchdb --save

PouchDB is a JavaScript project and it doesn't have proper support for TypeScript. This is not an issue as we can get around the hurdle of missing type definitions. However, we're going to need to obtain type definitions for another library that will load the plugin.

From the command line, execute the following:

npm install @type/node --save

Installing the Node.js type definitions will allow us to use the require keyword which is essential when importing JavaScript dependencies.

There is one more JavaScript dependency for our project, but this time we don't have to worry about the type definitions not existing. We need to install a library for generating unique id values which will represent our document keys. This library can be installed through the following command:

npm install uuid @type/uuid --save

The above command will install the library and its type definitions for TypeScript.

At this point we can focus on the development of our application!

Developing the Angular 2 Shared Provider

It is good practice in any Angular 2 application to keep database related activity separated in what is known as a shared provider or shared service. This allows us to have a singleton database instance and keep our database code out of the page logic.

To create a provider in Ionic 2, execute the following command:

ionic g provider pouchdb-provider

The above command should create a file at src/providers/pouchdb-provider.ts that we can work with. Open this new file and include the following code:

import { Injectable, EventEmitter } from '@angular/core'; var PouchDB = require("pouchdb"); @Injectable() export class PouchDBProvider { private isInstantiated: boolean; private database: any; private listener: EventEmitter<any> = new EventEmitter(); public constructor() { } public fetch() { } public get(id: string) { } public put(document: any, id: string) { } public sync(remote: string) { } public getChangeListener() { } }

Before we start populating each of the methods, let's break down what we have so far.

This provider will be injectable in each of the pages we wish to use it. Because PouchDB allows us to sync changes we want to be able to emit those changes and subscribe to them, thus the need for the EventImitter import. The PouchDB library is imported like standard JavaScript.

We only want to keep one database instance open and we can do that by making use of an isInstantiated variable. This is done in the constructor method like so:

public constructor() { if(!this.isInstantiated) { this.database = new PouchDB("nraboy"); this.isInstantiated = true; } }

With an open database we want to be able to work with the data. PouchDB has its own APIs for working with data, but being that it is vanilla JavaScript, certain things should be enhanced in our provider to make it more Angular 2 friendly.

public fetch() { return this.database.allDocs({include_docs: true}); }

Fetching all data can be done using the allDocs method in PouchDB. By including the include_docs property, the document data is included rather than just id values.

public get(id: string) { return this.database.get(id); }

If you know the id of the document you're after then you can do a lookup directly rather than querying for all the documents. Saving documents is where things can get a little confusing:

public put(document: any, id: string) { document._id = id; return this.get(id).then(result => { document._rev = result._rev; return this.database.put(document); }, error => { if(error.status == "404") { return this.database.put(document); } else { return new Promise((resolve, reject) => { reject(error); }); } }); }

In the above put method we have a way for creating or updating a document. Documents that have no revision are created, otherwise they are updated. This is possible by first doing a lookup on a document by its id. If the document exists, use the revision and update it, otherwise create it.

public sync(remote: string) { let remoteDatabase = new PouchDB(remote); this.database.sync(remoteDatabase, { live: true }).on('change', change => { this.listener.emit(change); }).on('error', error => { console.error(JSON.stringify(error)); }); }

Without synchronization you aren't technically using Couchbase in your stack. Also, there are much better solutions for data than PouchDB if you're only looking for a local database. With that said, the above sync function will allow you to connect to a remote database. Our remote database is actually a running Sync Gateway instance. We are choosing to do a two-way sync with Sync Gateway and emitting the changes to the EventEmitter in Angular 2.

public getChangeListener() { return this.listener; }

When we wish to subscribe to those changes we must first obtain a copy of the listener that is emitting them.

At this point the Angular 2 provider is complete, but not ready to be used. It needs to be added the project's @NgModule block found in the src/app/app.module.ts file. The file would look something like the following:

import { NgModule, ErrorHandler } from '@angular/core'; import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular'; import { MyApp } from './app.component'; import { HomePage } from '../pages/home/home'; import { PouchDBProvider } from "../providers/pouchdb-provider"; @NgModule({ declarations: [ MyApp, HomePage ], imports: [ IonicModule.forRoot(MyApp) ], bootstrap: [IonicApp], entryComponents: [ MyApp, HomePage ], providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}, PouchDBProvider] }) export class AppModule {}

Notice how the provider was imported and then added to the providers array. It can now be used in each page of our application.

Adding PouchDB Logic for Saving and Syncing

The rest of our application is actually quite simple now that we've laid the foundation to our data layer. Remember this is a very simple application, but heavily data driven.

Open the project's src/pages/home/home.ts file and include the following code:

import { Component, NgZone } from '@angular/core'; import { NavController, AlertController } from 'ionic-angular'; import { PouchDBProvider } from "../../providers/pouchdb-provider"; import * as Uuid from "uuid"; @Component({ selector: 'page-home', templateUrl: 'home.html' }) export class HomePage { public items: Array<any>; public constructor(public navCtrl: NavController, public alertCtrl: AlertController, private database: PouchDBProvider, private zone: NgZone) { this.items = []; } public ionViewDidEnter() { } public insert() { } }

In the above code we are importing various Ionic and Angular 2 components as well as our PouchDB provider and UUID library. Many of these components are injected in the constructor method. The constructor method also initializes our public array which will be bound to the UI. This array will contain the data we sync with PouchDB.

While we can initialize our variables in the constructor method, it is never a good idea to load data into them in the constructor method. Instead we should use the Ionic ionViewDidEnter method:

public ionViewDidEnter() { this.database.sync("http://192.168.57.1:4984/example"); this.database.getChangeListener().subscribe(data => { for(let i = 0; i < data.change.docs.length; i++) { this.zone.run(() => { this.items.push(data.change.docs[i]); }); } }); this.database.fetch().then(result => { this.items = []; for(let i = 0; i < result.rows.length; i++) { this.items.push(result.rows[i].doc); } }, error => { console.error(error); }); }

In the above ionViewDidEnter method we are starting the two-way sync with PouchDB and Couchbase Sync Gateway. The hostname is that of my currently running Couchbase Sync Gateway instance. The database does not need to match that of my local database.

When we are syncing we are also going to subscribe to the change listener. This is a simple application so we won't worry about changing or deleting data, only adding data. Listeners can be a little iffy in Angular 2 where as there becomes a disconnect between the UI and the data. This disconnect can be corrected by adding the listener data into a zone.run method.

With the application subscribing to changes, we need to do a first-time query of the data when the application opens. We want to fetch all the data and add it to our public array.

The last method we have is the insert method and it is mostly Ionic driven:

public insert() { let prompt = this.alertCtrl.create({ title: 'Todo Items', message: "Add a new item to the todo list", inputs: [ { name: 'title', placeholder: 'Title' }, ], buttons: [ { text: 'Cancel', handler: data => {} }, { text: 'Save', handler: data => { this.database.put({type: "list", title: data.title}, Uuid.v4()); } } ] }); prompt.present(); }

Here we craft a prompt popup. When the save button is pressed, the text in the form is saved with PouchDB. The id of the document is uniquely generated with the UUID library.

So what does our UI look like?

Creating a Simple UI with HTML

The UI is the easy part because the application is so simplistic. Open the project's src/pages/home/home.html file and include the following HTML markup:

<ion-header> <ion-navbar> <ion-title> PouchDB w/ Ionic 2 </ion-title> <ion-buttons end> <button ion-button icon-only (click)="insert()"> <ion-icon name="add"></ion-icon> </button> </ion-buttons> </ion-navbar> </ion-header> <ion-content padding> <ion-list> <ion-item *ngFor="let item of items"> {{ item.title }} </ion-item> </ion-list> </ion-content>

The UI has an action bar with a button, that when pressed will trigger the insert method found in our TypeScript. The core content of the UI is a list that loops through the public array printing each item to the screen.

Taking the Project for a Test Run

We went over many concepts and a lot of code. To make life easier, I uploaded a working project to GitHub that you can download and test out for yourself.

With the project downloaded, execute the following commands to restore the dependencies, plugins, and platforms:

npm install ionic state restore

As long as you don't forget to update the sync method found in the project's src/pages/home/home.ts file with your Sync Gateway host, the project should be runnable.

Conclusion

You just saw how to create an Android and iOS application that syncs using Ionic 2, PouchDB, and Couchbase. This is an alternative method to theprevious guide that I wrote which uses Couchbase Lite.

本文数据库(综合)相关术语:系统安全软件

主题: JavaJavaScriptiOSAndroidNode.jsRESTHTMLGitHubGitWindows
分页:12
转载请注明
本文标题:Syncing with Couchbase in an Ionic 2 App using PouchDB
本站链接:http://www.codesec.net/view/519853.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 数据库(综合) | 评论(0) | 阅读(194)