不求谌解

不求谌解

💻 Web Dev / Creative 💗 ⚽ 🎧 🏓
twitter
github
jike
email

Implement a Vue pagination component by hand.

image

Requirements Analysis#

  • Customizable number of displayed page buttons
  • Customizable color theme for the pagination component
  • Option to choose between displaying text content or icons for pagination

Implementation#

  1. The number of displayed page buttons for the pagination component can be defined using the pagesToDisplay() computed property
  2. The color theme can be set by binding a paginationClass class that returns the desired color type
  3. The choice between text or icons can be implemented using nested conditional template statements

Pagination Principle#

Implementing pagination mainly relies on two parameters: total (total number of items) and perPage (number of items displayed per page). The backend can use these parameters to return the corresponding data to the frontend.
In the entire pagination component, the most complicated part is the logic for displaying the page button list. The page button list is an array that returns the [minimum page number, maximum page number]. Therefore, the problem is divided into how to find the maximum and minimum values of the page numbers in the page button list array. Two computed properties, maxPage() and minPage(), are defined to return the maximum and minimum values of the page numbers in the page button array, respectively.

Page Component#

<template>
  <ul class="pagination" :class="paginationClass">
    <!-- Previous page -->
    <li
      class="page-item prev-page"
      :class="{ disabled: value === 1,'no-arrows': noArrows }"
    >
      <a class="page-link" arial-label="Previous" @click="prevPage">
      <!-- Text content -->
        <template v-if="withText">
          prev
        </template>
      <!-- Arrow icon -->
        <i class="arrow-left" v-else></i>
      </a>
    </li>
    <!-- Currently displayed pagination list -->
    <li
      class="page-item"
      v-for="item in range(minPage, maxPage)"
      :key="item"
      :class="{active: value === item}"
    >
      <a class="page-link" @click="changePage(item)">{{item}}</a>
    </li>
    <!-- Next page -->
    <li
      class="paga-item next-page"
      :class="{disabled: value === totalPage, 'no-arrows': noArrows}" 
    >
      <a class="page-link" arial-label="Next" @click="nextPage">
      <!-- Text content -->
        <template v-if="withText">
          Next
        </template>
        <i class="arrow-right" v-else></i>
      </a>
    </li>
  </ul>
</template>

props#

props: {
  // Color theme
  type: {
    type: String,
    default: "primary",
    // Validate the value of `type`
    validator: value => {
      return [
        "default",
        "primary",
        "info",
        "success",
        "danger"
      ].includes(value);
    }
  },
  // Whether to include text content
  withText: Boolean,
  // Arrow icon
  noArrows: Boolean,
  // Number of pages
  pageCount: {
    type: Number,
    default: 0
  },
  // Number of items displayed per page
  perPage: {
    type: Number,
    default: 0
  },
  // Total number of items
  total: {
    type: Number,
    default: 0
  },
  // Current page number value
  value: {
    type: Number,
    default: 1
  }
}

data#

data: {
  // Number of page numbers displayed in the page button list
  defaultPagesToDisplay: 5
}

computed#

computed: {
  // Color theme
  paginationClass() {
    return `pagination-${this.type}`
  },
  // Total number of pages
  toatalPages() {
    if (this.pageCount > 0) return this.pageCount;
    if (this.total > 0 ) {
      return Math.ceil(this.total / this.perPage);
    }
  },
  // Number of displayed page buttons
  pagesToDisplay() {
    if (this.totalPages > 0 && this.totalPages < this.defaultPagesToDisplay) {
      return this.totalPages;
    } else {
      return this.defaultPagesToDisplay;
    }
  },
  // Minimum page number in the page button list
  minPage() {
    if (this.value >= this.pagesToDisplay) {
      // Define an offset
      const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
      // Assumed maximum page number = current page number + offset
      const newMaxPage = this.value + pagesToAdd;
      if (newMaxPage > this.totalPages) {
        return totalPages - this.pagesToDisplay + 1;
      } else {
        // Current page number is in the middle of the page button list
        return this.value - pagesToAdd;
      }
    } else {
      // When the current page number is less than the default number of page buttons, the minimum page number returns 1
      return 1;
    }
  },
  // Maximum page number in the page button list
  maxPage() {
    if (this.value >= this.pagesToDisplay) {
      const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
      const newMaxPage = this.value + pagesToAdd;
      if (newMaxPage < this.totalPages) {
        return newMaxPage;
      } else {
        return totalPages;
      }
    } else {
      // When the current page number is less than the default number of page buttons, the maximum page number returns the number of displayed page buttons
      return this.pageToDisplay;
    }
  }
}

methods#

methods: {
  // Page button list array
  range(min, max) {
    let arr =[];
    for (let i = min; i <= max; i++) {
      arr.push(i);
    }
    return arr;
  },
  // Change page
  changePage(item) {
    this.$emit("input", item);
  },
  // Next page
  nextPage() {
    if (this.value < this.totalPages) {
      this.$emit("input", this.value + 1);
    }
  },
  // Previous page
  prevPage() {
    if (this.value > 1) {
      this.$emit("input", this.value - 1);
    }
  }
}

Conclusion#

To continuously improve one's technical skills, one must not only be satisfied with writing business components. In the process of writing independent components like pagination, it also made me think about the design of APIs and the complexity of functionality. No matter how complex a component is, it is mainly composed of props, events, and slots. The specific implementation mainly relies on the basic abilities of JavaScript.
Previously, I also wrote radio and input components based on element. I hope to continue writing these independent components and discover a bigger world. The source code for this pagination component can be found here.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.