小程序从0到1:微信全栈工程师一本通
上QQ阅读APP看书,第一时间看更新

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”获取下载地址。