第四课:数据模型和Observable

本节课中我们设计应用中将会用到的检查列表的数据模型,他会和Observable进行合作。数据模型不是Ionic 2特有的东西,模型是变成里面同样概念。根据内容的不同,模型的定义多变,但是总的来说模型就是用于存储和代表数据的。
在Ionic 2和Angular 2中,如果我们想持有某些数据的引用,我们大概需要这样去做:

  1. this.myDataArray = ['1', '2', '3'];

然后,如果我们是创建模型的话,那么大概是这样子的:

  1. this.myDataArray = [
  2. new MyDataModel('1'),
  3. new MyDataModel('2'),
  4. new MyDataModel('3')
  5. ];

所以,我们创建一个对象object来持有数据而不是纯存储。首先,可能比较难理解为什么我们要这么做,对于像上面例子这么简单的数据看起来很复杂,但是同时会带来很多好处。这个应用中带来的主要好处包括:

  • 允许我们清晰的定义数据结构
  • 允许我们在数据模型里面创建助理函数来操作数据
  • 允许我们在不同地方重用数据模型,简化代码

真切的希望本课能清晰的给你展示创建数据模型能够带来的好处,但是我体验说明一下这不是必需的。你也可以直接在类里面定义一些数据就可以了。
我们也在数据模型中会创建和用到我们自己的Observable,但是我们还是在遇到他的时候再说吧。

创建数据模型

通常如果我们想要创建一个数据模型的话我们将创建一个类来定义他(基本上就是个普通对象),然后定义一些助理函数,就像这样:

  1. class PersonModel {
  2. constructor(name, age){
  3. this.name = name;
  4. this.age = age;
  5. }
  6. increaseAge(){
  7. this.age++;
  8. }
  9. changeName(name){
  10. this.name = name;
  11. }
  12. }

然后,我们就可以从他创建以系列的实例(对象)了:

  1. let person1 = new PersonModel('Jason', 43);
  2. let person2 = new PersonModel('Louise', 22);

然后实例有可以分别去调用助理函数:

  1. person1.increaseAge();

Ionic 2的理念也差不多,除了Ionic 2/Angular 2中我们需要创建一个Injectable(基础里面讲过)。记住Injectable用于创建服务,他创建的服务可以注入到任何的其他组件中,所以当我们想要使用数据模型的时候,我们直接注入进去就可以了。
我们看一下实际的数据模型大概是什么样子的,然后再学习之。
> 修改 src/models/checklist-model.ts为以下

  1. export class ChecklistModel {
  2. checklist: any;
  3. checklistObserver: any;
  4. constructor(public title: string, public items: any[]){
  5. this.items = items;
  6. }
  7. addItem(item): void {
  8. this.items.push({
  9. title: item,
  10. checked: false
  11. });
  12. }
  13. removeItem(item): void {
  14. let index = this.items.indexOf(item);
  15. if(index > -1){
  16. this.items.splice(index, 1);
  17. }
  18. }
  19. renameItem(item, title): void {
  20. let index = this.items.indexOf(item);
  21. if(index > -1){
  22. this.items[index].title = title;
  23. }
  24. }
  25. setTitle(title): void {
  26. this.title = title;
  27. }
  28. toggleItem(item): void {
  29. item.checked = !item.checked;
  30. }
  31. }

我们这个数据模型所作的实际上是创建了一个单独的检查列表的蓝图。一个检查列表有一个标题和任意数量的需要完成的项。所以,我们设置了成员变量来持有这些值:title是一个简单的字符串,items是一个数组。
注意,我们允许通过构造器传入标题和项。创建一个新的检查列表的时候必须传入标题,但是项的数组是可选的。如果你想立刻给检查列表添加项的话,我们可以在初始化的时候传入,否则将会初始化一个空的数组。
我们也创建了大量的助理函数,看起来都挺直白的,他们允许我们改变检查列表的标题,或者修改检查列表的项目(修改名字,移除项,给检查列表添加新的项,设置项的完成状态)。
同时主题我们有在每个函数后面加上:void。就像我们声明变量的时候指定类型一样:

  1. checklist: any

我们也可以声明函数的返回值类型。在本案例中,没有返回值,所有我们用的是void。如果其中一个函数返回字符串的话,那么我们就用:string代替。
设置完以上之后,我们可以在任何导入了Checklist Model(下一课会讲)的组件里简单的通过以下代码创建一个新的检查列表:

  1. let newChecklist = new ChecklistModel('My Checklist', []);

或者

  1. let newChecklist = new ChecklistModel('My Checklist', myItemsArray);

我们现在将做些更好玩的,在数据模型中使用Observable,这样我们就可以在检查列表修改的时候被告知(此时我们可以触发保存数据到内存)。

添加一个Observable

在本课程的基础部分我们有了解一点Observable — 我们使用Http服务返回的Observable来刷新一下你的记忆:

  1. this.http.get('https://www.reddit.com/r/gifs/new/.json?limit=10').map(res => res.json()).subscribe(data => {
  2. console.log(data);
  3. });

我们调用了get方法,然后订阅subscribe他返回的Observable。记住,Observable和Promise不一样,他返回的是数据流,并且可以多次获得数据。这个理念无法通过Http服务来阐述,因为大部分情况下我们只需要获取一次数据。在Http案例中Observable也已经为我们建好了。
我们也将在数据模型中从头到尾的创建我们自己的Observable,这样当检查列表发生改变的时候(因为我们在发生改变的时候会发出一些数据)我们可以在应用的其他部分可以监听到。在实施Observable的时候,你可以看到如何从头创建一个observable,你也可以看到一个Observable如何多次发出数据。
实施之前,我们更详细的讲一下Observable,也就是我们先要具体要做的内容。在以上的subscribe函数代码中,我们只处理了一次response:

  1. this.http.get(url).subscribe(data => {
  2. console.log(data);
  3. });

这实际上就是Observable的 onNext 响应。Observable同时也提供了两个其他的响应,onError和onCompleted,如果我们愿意的话可以处理全部:

  1. this.http.get(url).subscribe(
  2. (data) => {
  3. console.log(data);
  4. },
  5. (err) => {
  6. console.log(err);
  7. },
  8. () => {
  9. console.log("completed");
  10. }
  11. );

以上代码第一个事件处理器处理的是 onNext 想要,基本意思就是“当我们侦测到从流里传来下一点数据的时候,那么就调用这里”。第二个处理器处理的是 onError 想要,你可能猜到了他是当有错误发生的时候就会触发。最后的处理器处理的是 onCompleted 事件,他是当Observable返回所有数据后触发的。
这里最有用的是 onNext了,如果我们创建自己的observable,我们可以通过调用Observable的 next 提供一些数据多次触发 onNext 响应。
现在,理论的道路清理完毕,我们看一下具体实现。
> 修改 src/models/checklist-model.ts为如下

  1. import {Observable} from 'rxjs/Observable';
  2. export class ChecklistModel {
  3. checklist: any;
  4. checklistObserver: any;
  5. constructor(public title: string, public items: any[]){
  6. this.items = items;
  7. this.checklist = Observable.create(observer => {
  8. this.checklistObserver = observer;
  9. });
  10. }
  11. addItem(item): void {
  12. this.items.push({
  13. title: item,
  14. checked: false
  15. });
  16. this.checklistObserver.next(true);
  17. }
  18. removeItem(item): void {
  19. let index = this.items.indexOf(item);
  20. if(index > -1){
  21. this.items.splice(index, 1);
  22. }
  23. this.checklistObserver.next(true);
  24. }
  25. renameItem(item, title): void {
  26. let index = this.items.indexOf(item);
  27. if(index > -1){
  28. this.items[index].title = title;
  29. }
  30. this.checklistObserver.next(true);
  31. }
  32. setTitle(title): void {
  33. this.title = title;
  34. this.checklistObserver.next(true);
  35. }
  36. toggleItem(item): void {
  37. item.checked = !item.checked;
  38. this.checklistObserver.next(true);
  39. }
  40. }

此处第一件要关心的事情是我们从RxJS库里导入了Observable。然后在构造器中,我们设置了Observable:

  1. this.checklist = Observable.create(observer => {
  2. this.checklistObserver = observer;
  3. });

代码里面的this.checklist成员变量是我们自己的observable。由于他是一个observable,我们可以对他进行订阅,由于他是我们数据模型的一部分,我们可以在任何检查列表上订阅他。例如:

  1. let newChecklist = new ChecklistModel('My Checklist', []);
  2. newChecklist.checklist.subscribe(data => {
  3. console.log(data);
  4. });

当然,我们还没有用Observable来做什么,所以他永远都不会触发onNext响应。这就是为什么我们在每个助理函数里面添加如下代码片的原因:

  1. this.checklistObserver.next(true);

这样,无论何时我们通过助理函数来更改标题,或者添加一个新的项,或者其他任何事情的时候,他都会通知所有订阅了他的Observable的任何事物。我们只要知道发生了改变所以我们只要传入一个boolean(true或者false),但是如果我们想要的话我们也可以传入一些数据。
这样的结果就是现在我们可以“观察observe”我们创建的检查列表的变动。稍后我们将用到这些变更监听然后触发保存操作。

总结

本课程中我们稍微超出了入门者级别,创建了强壮的(robust,据说有人翻译为鲁棒性)数据模型。如我之前讲到,他当然有他的好处,但是如果学习本课程遇到麻烦的时候也不要被吓到 — 作为初学者的你可以直接在类上定义数据,不要操心数据模型和observable。
我真心不想用Observable吓坏你 — 他们很容易混淆(直到你脑子里面适应他们),在除了Http服务订阅响应之外,在大部分的应用中你真心不需要用到它们。但是,一旦你理解了他们,你就可以用他们来做更强大的东西。
尽管本课程稍微高级一点,他还是很好的向你展示了如何在项目中使用Observable,如果想跟上课程进度,希望接下来的内容不会烦到你。