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:

  1. ** Page: ** Triggers the onReachBottom event to modify the Pagination componentkey
  2. Component: keyValue listens for changes and triggers a load event_loadMoreData
  3. Component:_loadMoreDataWhen the conditions are met, the loading list function is triggeredthis.triggerEvent('getList')And pass in the page parameters page and size.
  4. ** Page: ** Fetch data from the Model layer.
  5. ** Page: ** Gets the data and passes it to the Pagination componentlistandtotalValue.
  6. Component:listListen 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.