The project encountered TAB switching list, each TAB needs paging, paging process has a similar, so I want to package paging as a component, convenient application.
The application of the component has been written as a small demo that looks like this (the data is mock) :
Source code can be viewed: wxapp-pagination
The project requirements
Specific project requirements:
- See your meetings (page named Meetings)
- TAB switching, divided into:
- “My meeting” (the meeting I am attending is distinguished by “join” as key)
- “My appointment” (the meeting I booked, marked with “book” as key)
- Page = page +1 (size=10)
Of course, as a front-end, there are performance requirements to consider:
- For the first time, only the front page of the default TAB page is loaded. Other tabs are loaded only when corresponding tabs are clicked.
- Return to the loaded TAB page and keep the original data without reloading.
So the prototype would look something like this:
Logic implementation
The project structure associated with paging logic is as follows:
Heavy Metal Exercises ── Heavy Metal Exercises ─ Meeting -item# list item│ └ ─ ─ pagination# pagination component├── ├─ ├.js# My related model└── pages │ ├ ─# My related page│ ├ ─ ─ the meetings# my meetings│ └ ─ ─... │ └ ─ ─ vant - weappCopy the code
Let’s draw a picture of their relationship:
Listen for triggering paging events within the component
The event that triggers pagination is scrolling to the bottom of the Page. In the applet, this event is triggered by the onReachBottom event of the Page Page, but this event can only be triggered within the Page, so pass this trigger timing to the Pagination component.
Every time the onReachBottom event is triggered, set the component key to a random string. When component Pagination listens for key changes, pagination is performed.
// components/pagination/index.js
Component({
properties: {
key: {
type: String.observer: '_loadMoreData' // _loadMoreData is a paging operation}}})Copy the code
<! -- pages/user/meetings/index.wxml -->
<tabs active="{{currentTab}}" bind:change="onChange">
<tab title="My meeting" data-key="{{type['JOIN']}}">
<view class="meeting-list">
<pagination
name="JOIN"
key="{{joinKey}}"
>
</pagination>
</view>
</tab>
<tab title="My appointment.">
<view class="meeting-list">
<pagination
name="BOOK"
key="{{bookKey}}"
>
</pagination>
</view>
</tab>
</tabs>
Copy the code
Page({
onReachBottom(){
const key = scene[+this.data.currentTab].key // The corresponding TAB corresponds to the key
this.setData({
[key]: random(16)})}})Copy the code
Implementation logic for the paging component
After the trigger reaches the bottom, the data needs to be loaded. However, before reloading, the loading conditions should be met:
- The previous page was not loaded (loading = true)
- The current page is finished loading (ended = true) and does not continue loading
The specific loading process is as follows:
- ** Page: ** Triggers the onReachBottom event to modify the Pagination component
key
值 - Component:
key
Value listens for changes and triggers a load event_loadMoreData
- Component:
_loadMoreData
When the conditions are met, the loading list function is triggeredthis.triggerEvent('getList')
And pass in the page parameters page and size. - ** Page: ** Fetch data from the Model layer.
- ** Page: ** Gets the data and passes it to the Pagination component
list
andtotal
Value. - Component:
list
Listen for changes and determine whether the load is complete.
component
// components/pagination/index.js
Component({
properties: {
name: String.key: {
type: String.observer: '_loadMoreData' // _loadMoreData is a paging operation
},
size: { // The number of items loaded each time
type: Number.value: 10
},
total: Number.// Total number of pages
list: { // Entries have been loaded
type: Array.observer: '_endState' // Check whether all the new data is loaded}},data: {
page: 0.// What is the current page
loading: false.// Whether it is loading
ended: false // Check whether all data is loaded
},
methods: {
_loadMoreData(){
const { loading, ended, size } = this.data
if (loading) return // The previous page has not been loaded
if (ended) return // The current page is not loaded
const page = this.data.page + 1
this.setData({
loading: true.// Start loading a new page
page
})
// Triggers the loading of the next page, passing in arguments
this.triggerEvent('getList', {
page,
size
})
},
_endState(val, oldVal) {
const { total, list } = this.properties
let ended = false
// Check whether all data is loaded
if (list.length >= total) {
ended = true
}
this.setData({
loading: false.// The current page is loaded. Loading is set to false
ended
})
}
}
})
Copy the code
page
<! -- pages/user/meetings/index.wxml -->
<pagination
name="BOOK"
key="{{bookKey}}"
bind:getList="getBookMeetings"
list="{{bookMeetings}}"
total="{{bookTotal}}"
>
</pagination>
Copy the code
Loop list item
The Pagination component gets a list of looping items. The initial idea is to loop the slot, but no item object is retrieved from the slot:
<view wx:for="{{list}}" wx:for-item="item" wx:key="index">
<slot></slot>
</view>
Copy the code
The solution is to encapsulate each list item as a component and loop through the abstract nodes so that pagination of other pages is extensible.
Declare in the componentGenerics field:
// components/pagination/index.json
{
"componentGenerics": {
"selectable": true
},
// ...
}
Copy the code
Using abstract nodes:
<! -- components/pagination/index.wxml -->
<view wx:for="{{list}}" wx:for-item="item" wx:key="index">
<selectable item="{{item}}"></selectable>
</view>
Copy the code
Specify which component selectable is:
<! -- pages/user/meetings/index.wxml -->
<pagination
generic:selectable="meeting-item"
// .Other properties >
</pagination>
Copy the code
Corresponding JSON file defines corresponding usingComponents:
// pages/user/meetings/index.json
{
"usingComponents": {
"pagination":"/components/pagination/index",
"meeting-item":"/components/meeting-item/index"
}
}
Copy the code
The meeting-item component receives a property item, so that in the meeting-item component, the item value of the loop list can be retrieved and the page can be drawn normally.
Switch lazy loading
Add the initImmediately attribute to Pagination. When initImmediately is true, the page is first loaded and the variable initState is used to indicate whether the page has already been initialized.
// components/pagination/index.js
Component({
properties: {
initImmediately: {
type: Boolean.value: true.observer: function(val){
if (val && !this.data.initState) {
this.initData()
}
}
},
// ...
},
data: {
initState: false.// Whether it has been loaded
// ...
},
lifetimes: {
attached: function () {
if (this.data.initImmediately){
this.initData()
}
},
},
methods: {
initData(){
console.info(`The ${this.data.name}:start init data`)
this._loadMoreData()
this.setData({
initState: true})},// ...
_endState(val, oldVal) {
if (!this.data.initState) return
// ...}}})Copy the code
Set initImmediately to true when currentTab is the current type.
<! -- pages/user/meetings/index.wxml -->
<pagination
name="JOIN"
init-immediately="{{currentTab==type['JOIN']}}"
// .
>
</pagination>
<pagination
name="BOOK"
init-immediately="{{currentTab==type['BOOK']}}"
// .
>
</pagination>
Copy the code
Component reuse
After completing the above components, you can reuse them directly on other paged pages. For example, to implement a page-fetching list of all users, simply add a user-item component and call it like this:
<pagination
name="USER"
key="{{key}}"
bind:getList="getUserList"
list="{{userList}}"
total="{{userTotal}}"
generic:selectable="user-item"
>
</pagination>
Copy the code
You can check out the small demo I wrote: Wxapp-Pagination.