第六课:保存和加载数据

你知道什么是最烦恼的吗?如果你创建了一大个待完成的checklist,稍后回来的时候再用的时候,发现没有了。嗯,这就是应用当前的状态,所以我们将要给用户提供一个保存用户数据的途径。
我们已经为他做了一些架构准备,我们订阅了我们的Observable在每次数据变更的时候都调用了save函数,我们只需要现在来实现这个函数就可以了。
> 修改 src/pages/home/home.ts 里面的 save 方法:

  1. save(): void {
  2. Keyboard.close();
  3. this.dataService.save(this.checklists);
  4. }

如果你不健忘的话,早些时候我们已经生成和导入了一个数据服务,所以我们这里只需要调用他并传入checklists数据。当然,我们目前还没有实现这个数据服务,所以他现在不会对数据做任何事情,我们现在就来弥补他。注意,我们也调用了Keyboard的close方法,由于会在任何数据项变更的时候调用save方法,我们可以用他来在用户添加或者编辑完数据之后确保键盘关闭。

保存数据

我们现在要给data.ts码代码了,让他可以将传入的任何数据保存到存储里面去。实际上这个服务的代码相当的简单,我们来看看:
> 修改 src/providers/data.ts :

  1. import { Storage } from '@ionic/storage';
  2. import { Injectable } from '@angular/core';
  3. @Injectable()
  4. export class Data {
  5. constructor(public storage: Storage){
  6. }
  7. getData(): Promise<any> {
  8. return this.storage.get('checklists');
  9. }
  10. save(data): void {
  11. let saveData = [];
  12. //Remove observables
  13. data.forEach((checklist) => {
  14. saveData.push({
  15. title: checklist.title,
  16. items: checklist.items
  17. });
  18. });
  19. let newData = JSON.stringify(saveData);
  20. this.storage.set('checklists', newData);
  21. }
  22. }

这里有个我们之前没见过的导入:

  1. import { Storage } from '@ionic/storage';

Storage是Ionic的统一存储服务,他负责使用最好的方式来存储数据的同时提供了一个统一的API给供我们使用。
当在设备上运行的时候,如果SQLite插件是可用状态的话(我们之前安装过了),他将会使用本地(native)SQLite数据库来存储数据。由于SQLite数据库只在设备上可用,在SQLite不可用的情况下,Storage也会用IndexedDB,WebSQL或者标准浏览器的localStorage。
尽可能使用SQLite,因为基于浏览器的本地存储不大可靠可以被操作系统默默的清理掉。如果你的数据可以被随机清理掉的话显然很不理想。
我们来看看getData函数:

  1. getData(): Promise<any> {
  2. return this.storage.get('checklists');
  3. }

这个函数允许我们获取最新的存储数据,他会以Promise的方式返回数据。我们将此函数的返回的Promise的返回类型设置为类型,这是一个更复杂的类型。记住,这不是唯一可用类型,所以,如果你觉得很迷惑的话,你可以什么都不加,像这样:

  1. getData(){
  2. return this.storage.get('checklists');
  3. }

注意,在这里我们没有给promise完成的时候添加处理器,我们只是返回get方法的结果(一个解析当前存储数据的promise)。记住,这个行为不是瞬间完成的,这样我们可以在调用这个函数的任何地方添加处理器,也更像应用的工作流。(希望这个能够更简单明了)
然后,我们来到了saveData函数,这个函数用于将数据保存到存储里:

  1. save(data): void {
  2. let saveData = [];
  3. //Remove observables
  4. data.forEach((checklist) => {
  5. saveData.push({
  6. title: checklist.title,
  7. items: checklist.items
  8. });
  9. });
  10. let newData = JSON.stringify(saveData);
  11. this.storage.set('checklists', newData);
  12. }

之前提到我们,我们将数据存为一个简单的JSON编码字符串,所以我们调用了JSON.stringify函数然后使用set方法将他保存到存储对象里去。在做这些之前,我们把数据的observable移除掉只加入title和items,因为他们跟JSON不搭调(他会引起circular对象错误),我们后面会重新创建它们。
这就是保存数据的全部内容,也没那么复杂。我们接下来处理加载数据到应用中。

加载数据

任何时候当用户打开应用,我们都需要从存储中加载checklists数据,所以最适合做这个操作的地方是home页的ionViewDidLoad,这个函数会在页面加载完成的时候触发。我们将对构造器进行小小的变更。
> 在 src/pages/home/home.ts 中修改constructor和ionViewDidLoad:

  1. constructor(public nav: NavController, public dataService: Data, public
  2. alertCtrl: AlertController, public platform: Platform) {
  3. }
  4. ionViewDidLoad(){
  5. this.platform.ready().then(() => {
  6. this.dataService.getData().then((checklists) => {
  7. let savedChecklists: any = false;
  8. if(typeof(checklists) != "undefined"){
  9. savedChecklists = JSON.parse(checklists);
  10. }
  11. if(savedChecklists){
  12. savedChecklists.forEach((savedChecklist) => {
  13. let loadChecklist = new ChecklistModel(savedChecklist.title, savedChecklist.items);
  14. this.checklists.push(loadChecklist);
  15. loadChecklist.checklist.subscribe(update => {
  16. this.save();
  17. });
  18. });
  19. }
  20. });
  21. });
  22. }

我们调用了数据服务里面刚定义的getData函数。之前也提过,getData函数返回一个promise而不是直接返回数据,这样我们可以在加载完成之后处理响应。如果getData直接返回数据的话,当我们想要访问他的时候可能他都没有返回。
所以我们一直等待取回数据,然后将checklists传入到我们的处理器。首先我们解码JSON字符串到数组到这样我们就可以用来,然后我们循环数组里的每个数据项,基于这些数据创建一个新的Checklist Data。循环数据和创建新的模型而不是直接将this.checklists设置为savedChecklists的原因是在保存checklists将他转换成JSON字符串的时候他就丧失了使用数据模型里的助理函数的能力。所以,我们得利用取得的标题和数据项来为所有的checklists创建新的对象。
最后,我们重新给Observable设置监听器,这样当数据改变的时候就会被处罚。重点关注我们所作的这些都是在platform.ready()调用里面进行的,如果在设备准备好之前这么做的话将会发生问题。

总结

这就是本节课的所有内容,数据现在在进行任何变更后可以被保存到SQLite数据里面去,应用重新打开的时候,所有数据将会重新加载回来。试一下添加一些checklist或者修改checklist的状态,然后重新加载应用看看对他进行的变更是否还在。