美文网首页ionic2
[Ionic 2从入门到精通] 7.6 本地和远程PouchDB

[Ionic 2从入门到精通] 7.6 本地和远程PouchDB

作者: 老牛啃码 | 来源:发表于2018-05-17 20:05 被阅读1次

    虽然我们完成了应用的大部分功能,但是本节课将是最大最难的那个。我们将使用PouchDB来存储信息而不是直接将他扔到信息数组里面去。
    PouchDB是一个浏览器内的NoSQL数据库,灵感来自CouchDB项目。他最大的功能是允许存储离线数据,当应用再次上线之后会自动从远程数据库同步数据。和使用Ionic提供的SqlStorage一样,使用PouchDB保证你存储的本地数据不会被随机擦除。
    普及NoSQL不是本书的目的,但是为了给你一点基础,NoSQL数据库通常以类JSON的键值对风格格式来存储数据,不同的样式要不同的去思考。所以,所以当你来到NoSQL的时候,需要放弃你可能已有大量的的SQL固有观念(假设你之前用过)。我们存储的数据非常简单,所以我们不需要担心如何合理的使用NoSQL数据库。
    所以PouchDB会负责本地数据的存储,但是我们还要用到Cloudant来创建一个远程数据库。我们将同步本地的PouchDB数据库和远程的Cloudant数据库,这样无论何时应用上线的时候都可以从Cloudant获取最新数据,任何本地新数据都将推送到Cloudant。幸运的是,这两个工具都被设计为相互协作且设置好了远程同步,这在平常事一个非常复杂的任务,现在却变得非常简单了。
    Cloudant是一个DBaas(Database as a Service数据库即是服务),所以我们只要创建一个帐号(低端使用免费)来使用,当然也要设置数据库。使用类似Cloudant这样的的东西非常简单,且扩展非常简单(因为所有后端架构都帮你处理好了),但是如果你喜欢的话,你也可以轻松的使用类似CouchDB或者Couchbase安装到你的服务端来和PouchDB同步。这个我不会深入,因为差别不大。

    创建Cloudant Database

    在进入编码之前,我们要在IBM Bluemix的Cloudant上设置我们的后端。Bluemix让我们可以访问IBM的Open Cloud Architecture,他可以用来创建,部署和管理基于云的应用。他提供大量可用的服务,其中一个就是Cloudant。
    首先你的创建一个IBM Bluemix帐号。当你创建好帐号登录进去之后,可以看到这样的界面:

    Bluemix

    选择Create App选项,选择Mobile,给你的应用取个名字然后点击Finish。之后就会看到这样的画面:

    Bluemix

    从左边的Service菜单选择Cloudant NoSQL DB,然后在Cloudant Dashboard上点击View your Data。你现在应该来带了Cloudant的仪表盘了。点击右上的Create Database选项创建一个新的数据库然后命名为camperchat或者其他你喜欢的。

    Bluemix

    现在,选择刚才新建的数据库查看具体细节,应该可以看到如下画面:

    6.7.4.jpg

    点击右上的API连接就得到了你的Cloudant Database URL了,这个我们要稍后提供给PouchDB的,所以最好拿个本子记下来吧。同时需要去Permissions部分生产一个API密钥(确保给他提供Write和Replicate权限) -- 他可以让PouchDB访问你的数据库,使用API密钥比用你的用户名和密码好一些。请记住密码因为一旦离开屏幕你就再也找不到了。
    这里我们还有一件事情要做。我们需要激活CORS(Cross Origin Resource Sharing)这样我们就可以从我们的应用中向数据库发起请求了。来到左边菜单的Account,选择CORS然后选择All Domains(*) 选项。
    这样就在Cloudant上设置好了,可以跟PouchDB一起使用了。

    整合PouchDB

    之前我们创建好了Data服务,也只是存放了一点Facebook API返回的值。现在我们要扩展Data服务用来操作PouchDB的数据存储和获取。
    > 修改 src/providers/data.ts 为如下:

    import { Injectable } from '@angular/core';
    import PouchDB from 'pouchdb';
    
    @Injectable()
    export class Data {
        fbid: number;
        username: string;
        picture: string;
        db: any;
        data: any;
        cloudantUsername: string;
        cloudantPassword: string;
        remote: string;
    
        constructor(){
            this.db = new PouchDB('camperchat');
            this.cloudantUsername = 'YourAPIUsernameHere';
            this.cloudantPassword = 'YourAPIPasswordHere';
            this.remote = 'https://YOUR-URL-HERE-bluemix.cloudant.com/camperchat';
            //Set up PouchDB
            let options = {
                live: true,
                retry: true,
                continuous: true,
                auth: {
                    username: this.cloudantUsername,
                    password: this.cloudantPassword
                }
            };
            this.db.sync(this.remote, options);
        }
    
        addDocument(message){
        }
    
        getDocuments(){
        }
    
        handleChange(change){
        }
    }
    

    我们已经通过npm安装好了PouchDB,想要在这个服务里使用的话我们得先导入:

    import PouchDB from 'pouchdb';
    

    在构造器中,我们处理了PouchDB的设置和远程Cloudant数据库的同步。首先我们我们新建了一个PouchDB,或者获取一个已存在的引用:

    this.db = new PouchDB('camperchat');
    

    然后我们定义了一些用于连接到Cloudant数据库的变量:

    this.cloudantUsername = 'YourAPIUsernameHere';
    this.cloudantPassword = 'YourAPIPasswordHere';
    this.remote = 'https://YOUR-URL-HERE-bluemix.cloudant.com/camperchat';
    

    请记得要用你自己的Cloudant仪表盘上的参数替换上面这些值。接着我们创建了一个options对象来配置到Cloudant数据库的连接,然后我们调用了sync方法:

    this.db.sync(this.remote, options);
    

    这样将会设置从PouchDB数据库复制到Cloudant数据库,同时也会设置从Cloudant数据库到PouchDB数据库的复制。现在,如果我们给PouchDB添加一些数据的时候将会自动反射到远程Cloudant数据库,如果我们在远程Cloudant修改或者添加一些数据的时候,将会自动反射到我们的本地数据库。
    在那之后我们创建了三个空函数,我们现在就来实现。
    addDocument
    > 修改 addDocument 为如下:

    addDocument(message) {
        this.db.put(message);
    }
    

    我们只需要简单的调用数据库的put就可以向PouchDB添加文档(NoSQL条款里一个数据对象叫做一个“文档”,所以可以将文档想象成一小片数据,而不是一个Word文档)。这样我们可以向这个函数传入任何数据以存储到数据库。
    getDocument
    > 修改 getDocument 为如下:

    getDocuments(){
        return new Promise(resolve => {
            this.db.allDocs({
                include_docs: true,
                limit: 30,
                descending: true
            }).then((result) => {
                this.data = [];
    
                let docs = result.rows.map((row) => {
                    this.data.push(row.doc);
                });
    
                this.data.reverse();
                resolve(this.data);
    
                this.db.changes({live: true, since: 'now', include_docs:true}).on('change', (change) => {
                    this.handleChange(change);
                });
            }).catch((error) => {
                console.log(error);
            });
        });
    }
    

    这个函数用户获取数据库中的所有文档(记住,文档只是一个数据对象)。这是一个一部操作,所以我们将他包装到一个promise里,然后在数据返回的时候在resolve。他允许我们在应用内其他地方使用getDocuments().then()语法。通过调用allDocs可以获取所有文档,这里我们也提供了一些选项:

    include_docs: true,
    limit: 30,
    descending: true
    

    这里的include_docs看起来有点迷糊,但是我们还是要指定这个这样文档内的所有数据都将被返回(信息,图片等)。如果没有提供这个参数的话只会返回文档的id。我们也设置了一个30的显示,和一个降序,这样一来只返回最新的30个文档(聊天信息)。
    我们将这些结果传入到我们的处理器,每个返回的行我们都会压入到this.data数字。我们想让这些信息倒序这样的话最新的消息将会显示在最下面。之后我们resolve我们创建的promise然后回传数据,然后设置changes监听器。
    changes监听器在每次侦测到数据库变更的时候(例如当其他用户添加了一个聊天信息的时候)都将调用this.handleChange函数,change本身也会被传入到函数里。我们现在来定义。
    handleChange
    > 修改 handleChange 为如下:

    handleChange(change) {
        let changedDoc = null;
        let changedIndex = null;
        this.data.forEach((doc, index) => {
            if(doc._id === change.id){
                changedDoc = doc;
                changedIndex = index;
            }
        });
    
        //A document was deleted
        if(change.deleted){
            this.data.splice(changedIndex, 1);
        }
        else {
            //A document was updated
            if(changedDoc){
                this.data[changedIndex] = change.doc;
            }
            //A document was added
            else {
                this.data.push(change.doc);
            }
        }
    }
    

    我们将传入的change反射到我们的this.data数组,但是有点技巧。传入进来的change对象可能是一个文档更新了,新建了一个文档,或者删除了一个文档。
    检查文档删除非常简单只要检查他是否有deleted属性就可以了。但是辨别他是否是更新,我们需要检查我们是否有了相同id的文档(如果找到了的话就更新他),如果没有的话我们就知道是新增文档了(我们需要将他添加到数组)。
    现在我们完全设置好了Data服务,但是如果要使用他的话还有些事情要做。我们需要使用provider来存储新数据,而不是像现在这样直接将他添加到messages数组,同时在应用打开的时候我们需要加载最新的信息。我们现在就来完成这些。
    > 修改 src/page/home/home.ts 如下:

    import { Component, ViewChild } from '@angular/core';
    import { Data } from '../../providers/data';
    
    @Component({
        selector: 'page-home',
        templateUrl: 'home.html'
    })
    export class HomePage {
    
        @ViewChild('chat') chat: any;
        chatMessage: string = '';
        messages: any = [];
    
        constructor(public dataService: Data){
            this.dataService.getDocuments().then((data) => {
                this.messages = data;
                this.chat.scrollToBottom();
            });
        }
    
        sendMessage(): void {
            let message = {
                '_id': new Date().toJSON(),
                'fbid': this.dataService.fbid,
                'username': this.dataService.username,
                'picture': this.dataService.picture,
                'message': this.chatMessage
            };
    
            this.dataService.addDocument(message);
            this.chatMessage = '';
        }
    }
    

    现在我们在构造器中调用了getDocument()函数来加载信息数据,一旦完成之后我们通过@ViewChild获取滚动内容的引用,将他滚动到底部。
    最后,在sendMessage函数中,唯一的变更是我们调用了addDocument函数而不是直接把信息压入到messages数组。
    终于完成了!【译者:我尿也憋坏了】现在聊天应用的全部功能都开发完了。他能够正常工作了,但是也丑爆了。下节课我们让他变漂亮点甚至给他加点动画的啥的!

    相关文章

      网友评论

        本文标题:[Ionic 2从入门到精通] 7.6 本地和远程PouchDB

        本文链接:https://www.haomeiwen.com/subject/eiyidftx.html