第六课:保存和加载数据
你知道什么是最烦恼的吗?如果你创建了一大个待完成的checklist,稍后回来的时候再用的时候,发现没有了。嗯,这就是应用当前的状态,所以我们将要给用户提供一个保存用户数据的途径。
我们已经为他做了一些架构准备,我们订阅了我们的Observable在每次数据变更的时候都调用了save函数,我们只需要现在来实现这个函数就可以了。
> 修改 src/pages/home/home.ts 里面的 save 方法:
save(): void {
Keyboard.close();
this.dataService.save(this.checklists);
}
如果你不健忘的话,早些时候我们已经生成和导入了一个数据服务,所以我们这里只需要调用他并传入checklists数据。当然,我们目前还没有实现这个数据服务,所以他现在不会对数据做任何事情,我们现在就来弥补他。注意,我们也调用了Keyboard的close方法,由于会在任何数据项变更的时候调用save方法,我们可以用他来在用户添加或者编辑完数据之后确保键盘关闭。
保存数据
我们现在要给data.ts码代码了,让他可以将传入的任何数据保存到存储里面去。实际上这个服务的代码相当的简单,我们来看看:
> 修改 src/providers/data.ts :
import { Storage } from '@ionic/storage';
import { Injectable } from '@angular/core';
@Injectable()
export class Data {
constructor(public storage: Storage){
}
getData(): Promise<any> {
return this.storage.get('checklists');
}
save(data): void {
let saveData = [];
//Remove observables
data.forEach((checklist) => {
saveData.push({
title: checklist.title,
items: checklist.items
});
});
let newData = JSON.stringify(saveData);
this.storage.set('checklists', newData);
}
}
这里有个我们之前没见过的导入:
import { Storage } from '@ionic/storage';
Storage是Ionic的统一存储服务,他负责使用最好的方式来存储数据的同时提供了一个统一的API给供我们使用。
当在设备上运行的时候,如果SQLite插件是可用状态的话(我们之前安装过了),他将会使用本地(native)SQLite数据库来存储数据。由于SQLite数据库只在设备上可用,在SQLite不可用的情况下,Storage也会用IndexedDB,WebSQL或者标准浏览器的localStorage。
尽可能使用SQLite,因为基于浏览器的本地存储不大可靠可以被操作系统默默的清理掉。如果你的数据可以被随机清理掉的话显然很不理想。
我们来看看getData函数:
getData(): Promise<any> {
return this.storage.get('checklists');
}
这个函数允许我们获取最新的存储数据,他会以Promise的方式返回数据。我们将此函数的返回的Promise的返回类型设置为<any>类型,这是一个更复杂的类型。记住,这不是唯一可用类型,所以,如果你觉得很迷惑的话,你可以什么都不加,像这样:
getData(){
return this.storage.get('checklists');
}
注意,在这里我们没有给promise完成的时候添加处理器,我们只是返回get方法的结果(一个解析当前存储数据的promise)。记住,这个行为不是瞬间完成的,这样我们可以在调用这个函数的任何地方添加处理器,也更像应用的工作流。(希望这个能够更简单明了)
然后,我们来到了saveData函数,这个函数用于将数据保存到存储里:
save(data): void {
let saveData = [];
//Remove observables
data.forEach((checklist) => {
saveData.push({
title: checklist.title,
items: checklist.items
});
});
let newData = JSON.stringify(saveData);
this.storage.set('checklists', newData);
}
之前提到我们,我们将数据存为一个简单的JSON编码字符串,所以我们调用了JSON.stringify函数然后使用set方法将他保存到存储对象里去。在做这些之前,我们把数据的observable移除掉只加入title和items,因为他们跟JSON不搭调(他会引起circular对象错误),我们后面会重新创建它们。
这就是保存数据的全部内容,也没那么复杂。我们接下来处理加载数据到应用中。
加载数据
任何时候当用户打开应用,我们都需要从存储中加载checklists数据,所以最适合做这个操作的地方是home页的ionViewDidLoad,这个函数会在页面加载完成的时候触发。我们将对构造器进行小小的变更。
> 在 src/pages/home/home.ts 中修改constructor和ionViewDidLoad:
constructor(public nav: NavController, public dataService: Data, public
alertCtrl: AlertController, public platform: Platform) {
}
ionViewDidLoad(){
this.platform.ready().then(() => {
this.dataService.getData().then((checklists) => {
let savedChecklists: any = false;
if(typeof(checklists) != "undefined"){
savedChecklists = JSON.parse(checklists);
}
if(savedChecklists){
savedChecklists.forEach((savedChecklist) => {
let loadChecklist = new ChecklistModel(savedChecklist.title, savedChecklist.items);
this.checklists.push(loadChecklist);
loadChecklist.checklist.subscribe(update => {
this.save();
});
});
}
});
});
}
我们调用了数据服务里面刚定义的getData函数。之前也提过,getData函数返回一个promise而不是直接返回数据,这样我们可以在加载完成之后处理响应。如果getData直接返回数据的话,当我们想要访问他的时候可能他都没有返回。
所以我们一直等待取回数据,然后将checklists传入到我们的处理器。首先我们解码JSON字符串到数组到这样我们就可以用来,然后我们循环数组里的每个数据项,基于这些数据创建一个新的Checklist Data。循环数据和创建新的模型而不是直接将this.checklists设置为savedChecklists的原因是在保存checklists将他转换成JSON字符串的时候他就丧失了使用数据模型里的助理函数的能力。所以,我们得利用取得的标题和数据项来为所有的checklists创建新的对象。
最后,我们重新给Observable设置监听器,这样当数据改变的时候就会被处罚。重点关注我们所作的这些都是在platform.ready()调用里面进行的,如果在设备准备好之前这么做的话将会发生问题。
总结
这就是本节课的所有内容,数据现在在进行任何变更后可以被保存到SQLite数据里面去,应用重新打开的时候,所有数据将会重新加载回来。试一下添加一些checklist或者修改checklist的状态,然后重新加载应用看看对他进行的变更是否还在。