
2.6 实现电影列表页
本节将实现一个可以上下滚动的列表,当滚动到底部时提示“继续滑动加载更多”,如图2-20所示。

图2-20
2.6.1 使用finally方法
使用2.1.3节快速创建页面的方法,创建pages/douban/list页面,用于展示电影榜单列表。
在文档树中打开pages/douban/list.js文件,修改data变量为:
data: { type: 'in_theaters', page: 1, size: 20, total: 1, movies: [] }
其中,type是调用豆瓣API时需要用到的电影类型。page是分页的页码,代表当前是第几页,size代表每次最多拉取多少条数据,total需要从豆瓣服务器获取,默认设置为1, movies是拉取到的数据。由于允许查看多页内容,因此movies是一个累加数组。
然后,在页面数据变量data下方添加一个retrieve方法:
retrieve() { let app = getApp() let start = (this.data.page - 1) * this.data.size wx.showLoading({ title: ’加载中’ }) return app.request(`https://api.douban.com/v2/movie/${this.data.type}? start= ${start}&count=${this.data.size}`) .then(res => { if (res.subjects.length) { let movies = this.data.movies.concat(res.subjects) this.setData({ movies: movies, total: res.total }) wx.setNavigationBarTitle({ title: res.title }) console.log(movies) } }).catch(err => { console.error(err) }).finally( ()=> { wx.hideLoading() }) }
这个方法用于从豆瓣API分页拉取数据,默认是从第1页拉取。如果当前页数大于总页数,则不再拉取。当前页数的改变在另一个函数中,稍后会看到。
app.request函数返回Promise对象,无论接口拉取失败还是成功,finally回调都会执行,所以在这里需要隐藏加载提示。wx.hideLoading是隐藏加载提示的界面交互API,与之相对应的,先于app.request调用的wx.showLoading是显示加载提示的API。
concat是js数组的方法,用于连接两个数组,拼接元素并返回新数组。
console.log(movies)这行代码用于在控制台窗口打印数据。
按<Command+B>组合键(或<Ctrl+B>组合键)刷新项目,或者等待工具自动刷新,在调试工具区域,查看Console面板,如图2-21所示,会看到数据输出。

图2-21
2.6.2 模板组件
在文档中选择douban文件夹并右击,从弹出的快捷菜单中选择“新建”命令,如图2-22所示。

图2-22
选择新建文件的类型为wxml,命名为list-template.wxml。下面就在这个文件中新建一个模板组件,然后在pages/douban/list.wxml页面中使用。
在文档树中打开list-template.wxml文件,修改wxml标签为:
<template name="list-template"> <scroll-view enable-back-to-top scroll-y bindscrolltolower="loadMorePage"> <view class="weui-panel"> <view class="weui-panel__bd"> <navigator wx:for="{{ movies }}" wx:key="{{ item.id }}" url="item? id= {{ item.id }}" class="weui-media-box weui-media-box_appmsg" hover- class= "weui-cell_active"> <view class="weui-media-box__hd weui-media-box__hd_in-appmsg" style="height:inherit; width:120rpx"> <image style="width: 128rpx; height: 168rpx; " class="weui- media-box__thumb" src="{{ item.images.small }}" /> </view> <view class="weui-media-box__bd weui-media-box__bd_in-appmsg"> <view class="weui-media-box__title">{{ item.title }}</view> <view class="weui-media-box__desc">{{ item.original_title }} ({{ item.year }})</view> <view class="weui-media-box__info"> 导演:<block wx:for="{{ item.directors }}" wx:key="{{ item.id }}"> {{ item.name }} </block> </view> </view> <view class="weui-media-box__ft"> <view class="weui-badge">{{ item.rating.average }}</view> </view> </navigator> </view> </view> <view class="weui-loadmore" wx:if="{{total>page}}"> <view class="weui-loadmore__tips">继续向下滑动加载更多内容</view> </view> </scroll-view> </template>
在一个文件中定义模板组件,以便于在其他页面中复用该组件。不过,在这个项目中,只有一个页面用到了该组件,定义该模板组件的目的仅在于说明定义方法。以下是上述代码的说明。
❑ template用于声明标签,name用于指定模板名称,该名称将在pages/douban/list. wxml中使用。
❑ enable-back-to-top属性用于实现单击标题栏回到顶部。
❑ bindscrolltolower用于绑定滑动到底部的事件,loadMorePage是一个尚未定义的方法。模板文件没有对应的js文件,稍后将在pages/douban/list.js中定义这个方法。
该模板组件中取用了三个模板变量:movies、page、total。它们将在模板被调用时传入。
在文档树中打开pages/douban/list.wxml文件,修改wxml标签为:
<import src="list-template"/> <template is="list-template" data="{{ movies, total, page }}"/>
import标签用于引入模板文件,src属性设置的是相对地址,因为list-template.wxml文件与list.wxml文件位于同一目录之下。在小程序中,对于wxml文件的引用,都不带扩展名。
在list-template.wxml中,template是定义标签,在list.wxml中是实例化标签,is指示模板名称,data是一个模板变量列表,是在模板中用到的变量,其中被传入的变量的顺序没有先后之分,“movies、total、page”与“total、page、movies”的效果是一样的。
2.6.3 加载更多
本节将实现向上滑动加载更多数据的功能。
实现加载更多的功能,需要用到loadMorePage方法,但在模拟文件list-template.wxml中使用的loadMorePage方法还没有被定义,因此在这个方法中需要将page加1,然后再次拉取分页数据。
打开pages/douban/list.js文件,添加loadMorePage函数,代码如下:
loadMorePage(){ if (this.data.page > this.data.total) return this.data.page++ this.retrieve() }
按<Command+B>组合键(或<Ctrl+B>组合键)测试一下,在列表页滑动页面至底部后继续向下滑动,发现并没有触发loadMorePage函数。这是为什么呢?
导航到列表页,在调试工具区打开Wxml面板,单击scroll-view这一行,如图2-23所示。

图2-23
此时,会发现列表的高度是2357px,这个高度明显超出了屏幕。这个高度是无法触发scrolltolower事件的,因为无法scroll到那里。再者,在2000+这个高度,scroll-view右侧没有出现滚动条,这是不正常的。我们猜想,此时的内容可以滚动,是因为page在滚动,并不是scroll-view在滚动。那么如何验证这个猜想呢?
打开pages/douban/list.wxml文件,在import这一行代码下面随意添加一些UI,例如:
<view class="weui-loadmore"> <view class="weui-loadmore__tips">测试滚动容器</view> </view>
再次测试,向上滚动,会发现新添加的测试UI会随电影列表一直向上滚动,如图2-24所示。

图2-24
这说明此时的滚动是page级别的滚动,scroll-view没有发挥作用。
删除测试UI,再在文档树中打开pages/douban/list-template.wxml文件,在scroll-view组件的style属性上添加内联样式,代码如下:
<scroll-view style="display:inline" enable-back- to-top scroll-y bindscrolltolower="loadMore Page"> ...
scroll-view默认的display样式是block,它会让元素显示为块状元素,这就使得高度将按实际大小来显示,于是滚动失效。inline代表将元素显示为内联元素,于是滚动条出现。
按<Command+B>组合键(或<Ctrl+B>组合键)刷新项目,会发现已经可以滚动,并能触发加载更多。
2.6.4 如何调试
到目前为止,已经实现了向上滚动加载更多的效果。但在测试中我们会发现存在如下两个问题。
❑ 一直可以触发加载更多,并且最后几页的数据已经重复。
❑“继续滑动加载更多”的提示一直存在。
通过console.log打印获取到的数据如图2-25所示。

图2-25
在Console面板中观察数据输出,如图2-26所示。

图2-26
多次测试可以发现,豆瓣API中返回的total数据并非指总页数,而是指总条目数。
在文档树中打开page/douban/list.js文件,修改retrieve函数,如图2-27所示。

图2-27
这样修改之后,就会先由总条目数和size计算一下总页数,然后再调用setData。Math. floor对小数点向下取整,返回不大于小数的最小整数。
再次测试,拉取重复数据的问题就解决了。
2.6.5 刷新视图
但是“继续滑动加载更多”的提示仍然存在,这是由于绑定在模板组件list-template上的page变量没有刷新。代码如下:
this.data.page++
这样的代码只是递增page的数值,绑定在list页面上的page变量并没有更新。
将retrieve函数中的这行代码:
this.setData({ movies: movies, total: total})
修改为:
this.setData({ movies: movies, total: total, page: this.data.page })
修改之后,page变量就会及时刷新,“继续滑动加载更多”的提示也不复存在。
setData在设置变量的时候会通知视图刷新这些变量。在修改之前,虽然total被刷新了,但是留在模板组件中的page变量一直是旧的数值1,所以下面这段代码中的if判断没有如期发挥作用:
<view class="weui-loadmore" wx:if="{{total > page}}"> <view class="weui-loadmore__tips">继续向上滑动加载更多内容</view> </view>
2.6.6 下载源码
在本节的学习过程中,如果遇到问题,可以通过如下方式来解决。
❑ 加入小程序微信群与作者及其他读者一同探讨。微信扫描前言中的二维码,关注“艺述思维”,发送“小程序”进群。
❑ 下载作者的源码,对照查找问题。在公众号中发送“豆豆电影2.6”获取下载地址。