4.8 数据绑定
在真实的项目中,业务数据通常都放置在自己的服务器中,然后通过HTTP请求来访问服务器提供的RESTFUl API,从而实现数据获取。
现在我们的post页面里的内容,全是一些被直接编码在wxml里的数据,这样的代码写法我们通常称为“硬编码”。这当然是一种非常不好的编码方法。
我们现在尝试将编码在post.wxml文件里的数据移植到post.js中,在post.js中加入一个临时变量postData用来模拟文章数据,并将上一小节中测试生命周期的代码移除。编写完成后的代码如下。
那么如何将data中的这些数据“填充”到页面中,并显示这些数据呢?
如果是开发传统的网页,肯定会使用以下思路:首先获取到HTML文档的DOM,然后对DOM标签进行赋值,从而实现数据的显示。但在小程序中,是没有DOM结构的,所以这个思路行不通。在许多流行的MVC或者MVVM框架中,比如AngularJS、Vue.js中,都有数据绑定的概念。小程序也借鉴了这些流行框架的思想,采用数据绑定的机制来做数据的初始化和更新。
只不过小程序做得更加决绝。AngularJS中,虽然官方不推荐使用DOM,但至少还有一个内置的jQLite用来支持获取DOM(虽然有很多的限制),开发者也可以自行集成jQuery。但小程序的脚本逻辑是运行在JSCore中,JSCore是一个没有DOM的环境,它完全抛弃了DOM结构,我们只能使用数据绑定来做数据的相关操作。
不同于AngularJS的双向数据绑定,小程序仅实现了单向数据绑定,即只支持从逻辑层传递到渲染层的数据绑定,反之则不可以。
小程序使用Page方法参数里的data变量作为数据绑定的桥梁。如代码清单4-13所示,data里已经被我们放置了一些数据,这些直接写在data里的数据,被称为数据绑定的初始化数据。
注意,数据绑定有以下两种:
• 一种是初始化数据的数据绑定,通常将这些数据直接写在Page方法参数的data对象下面。
• 另外一种是使用setData方法来做数据绑定,这种方式也可以理解为数据更新。这样的数据更新将引起页面的Rerender(重新渲染),参考图4-18中的Rerender。
4.8.1 初始化数据绑定
先来看看初始化数据绑定的写法。
代码清单4-13中,我们已经为Page方法的data对象填充了一些属性数据。现在,只需要对post.wxml文件做一些改动,即可让wxml能够“接收”这些初始化数据。
小程序使用Mustache语法双大括号{{}}在wxml组件里进行数据的绑定。我们试着用数据绑定的方式来显示《小时候的冰棍儿与雪糕》这篇文章,更改这篇文章的wxml代码,如代码清单4-14所示。注意,post.wxml文件里总共有3篇文章,但我们只更改了第一篇文章的相关代码,其他两篇文章依然使用硬编码的方式来填充数据。
保存后可以看到,页面并没有变化,第一篇文章的数据正常地显示了出来,这说明数据绑定成功了。
可以看到双大括号{{}}中,写入了一些变量名。细心的开发者应该发现{{}}里的变量名称同js文件里data对象的属性名称是相同的。可见,数据绑定非常简单,只要将data对象的属性名填入到双大括号{{}}中即可。MINA框架会自动在运行时用data数据替换这些{{}}。比如{{date}},在运行后将被替换为“Jan 28 2017”,而{{readingNum}}将被替换为“92”。
我们用图4-18页面生命周期图解这张图,解释一下初始化数据绑定的过程。
当页面执行了onShow函数后,逻辑层会收到一个通知(Notify);随后逻辑层会将data对象以json的形式发送到View视图层(Send Initial Data),视图层接收初始化数据后,开始渲染并显示初始化数据(First Render),最终将数据呈现在开发者的眼前。
这里需要注意,如果数据绑定是作用在组件的属性中,比如<image src="{{avatar}}" />,则一定要在{{}}外边加上双引号,否则小程序会报错。
如果是内容型的数据绑定,则不需要加双引号,比如<text>{{date}}</text>。
数据绑定的Mustache语法还有一些其他用法,我们依然会放在下面的实例项目中来讲解。
4.8.2 在哪里可以查看数据绑定对象
开发工具为我们提供了一个面板专门用来查看和调试数据绑定变量,这个面板就是在第2章中介绍的AppData面板。
我们来看一下Orange Can项目此时的AppData情况。打开【调试】→【AppData】,可以看到以下的数据情况,如图4-19所示。
图4-19 post页面在AppData面板中的数据绑定情况
请开发者注意,AppData面板对于调试和理解数据绑定有非常重要的作用,建议当开发者遇到数据绑定相关问题时,一定要首先打开这个面板来查看具体的数据绑定情况。
AppData下的数据以页面为组织单位。因为现在只在post页面里做了数据绑定,所以AppData下边只出现了pages/post/post这一个页面的数据。如果同时有多个页面进行了数据绑定,那么这里将出现多个页面的数据绑定情况。
如图4-19所示,可以看到在pages/post/post下显示了post页面的数据绑定变量情况,它的属性和post.js文件中所设置的data对象属性是一模一样的。
可以在这里更改某一项数据的值。更改是实时进行的,改变任何一个值,开发工具都能实时地将变化更新到模拟器UI里显示。开发者可以自行尝试,更改一下title、date或者content等的属性值,并注意观察模拟器的UI变化。
这里还有一个小技巧,让页面的数据以json的形式呈现:点击图4-19中的【Tree】这个选项,将打开如图4-20所示的面板。点击【Code】后,数据将以json的形式呈现,如图4-21所示。
图4-20 切换数据呈现形式
图4-21 以json的格式呈现数据
json格式的数据,非常利于我们快速复制这些数据。
4.8.3 绑定复杂对象
4.8.1小节中的Page参数下的data对象只是一个最简单的js对象,它的属性值都只是简单的文本与数字。在实际项目中,可能出现较为复杂的对象,将data对象更改为如下代码:
此时,data对象已不再是简单的对象,它的属性还包含有对象和数组。运行代码后,我们发现,小程序并不会报错,但UI上的数据无法正确显示。原因是被绑定的data对象数据结构改变后,相应的也需要在页面的wxml里做出和data数据结构等同的调整。调整之后的代码如下(注意,我们同样只更改第一篇文章的相关代码):
现在,date数据的绑定语法由{{date}}变成了{{object.date}};而collection数据的绑定语法由{{collectionNum}}变成了{{collectionNum.array[0]}}。这些相应的调整都是根据data数据结构的变化做出的,开发者请仔细对比。
重新运行项目,文章数据又可以正常显示了。
4.8.4 数据绑定更新
还可以使用setData函数来做数据绑定,这种方法可以理解为“数据更新”。setData方法位于Page对象的原型链上:Page.prototype.setData。大多数情况下,我们使用this.setData的方式来调用这个方法。
setData的参数接受一个对象,以key和value的形式将this.data中的key对应的值设置成value。
上面这句话要注意两点:
• setData会改变this.data变量里相同key的值。
• setData执行后会通知逻辑层执行Rerender,并立刻重新渲染视图,参考图4-18。
说起来好像很难理解,但使用起来非常简单,来看看具体代码。
在post页面中新增一个onLoad函数,并在其中执行setData,更改后的代码如下:
运行后我们发现,第一篇文章的标题由data里所设置的title: "小时候的冰棍儿与雪糕",被更改成了“一根雪糕的经济学原理”,但其他的数据并没有改变。原因在于我们使用this.setData只更新了title这一个数据,并未改变其他诸如date、avatar、content等数据。
此外,当执行了代码清单4-17后,此时this.data.title的值将是“一根雪糕的经济学原理”,而不再是“小时候的冰棍儿与雪糕”,因为this.setData的执行也会改变this.data里的值。
这就是setData的基本用法。此外,setData参数中的key是非常灵活的,来看看key可能出现的形式。修改代码清单4-17中的onLoad方法如下:
key可以使用字符串来表示,代码如下:
更改collectionNum数组子元素的值,代码如下:
更改object下的date的数值。
用this.setData所绑定或者更新的数据,并不要求在this.data中已预先定义。看看下面的例子,将post.js文件中的代码改为代码清单4-21所示。
上述代码中,去掉了this.data中的初始化数据,转而直接使用this.setData进行数据更新,从而实现数据绑定,这种方法也是可行的。
但这时项目并不能正常运行,UI上第一篇文章变成了一篇空白,且没有任何错误提示。原因在于,数据绑定的数据结构变了,wxml里的{{}}也需要做相应的改变。
借助我们之前讲到的【AppData】面板来看一下现在的数据绑定情况,如图4-22所示。
图4-22 AppData里的数据
很明显,这个数据结构和之前的是不一样的。所有的属性都被postData对象包裹了起来,因为我们在this.setData的时候指定的key是postData,value是文章的数据。所以,wxml里的{{}}需要如下这么改:
只需要在每个{{}}里加入postData即可。比如{{title}}应当改为{{postData.title}}。
请各位开发者注意,关于数据绑定的错误,小程序目前不会给出任何的错误提醒。如果你发现整个页面是空白的又没有错误消息,多半是数据绑定出了问题。这个时候AppData面板是最好的调试工具。