不求谌解

不求谌解

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

Implement a Vue pagination component

Requirement Analysis#

  • Customizable number of displayed page buttons
  • Customizable color theme for the pagination component
  • Option to display text content or icons for pagination operations

Implementation#

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

Pagination Principle#

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

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="page-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 it includes text content
  withText: Boolean,
  // Arrow icons
  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 list
  defaultPagesToDisplay: 5
}

Computed#

computed: {
  // Color theme
  paginationClass() {
    return `pagination-${this.type}`
  },
  // Total number of pages
  totalPages() {
    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 list
  minPage() {
    if (this.value >= this.pagesToDisplay) {
      // Define an offset
      const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
      // Assumed maximum page number = current page value + offset
      const newMaxPage = this.value + pagesToAdd;
      if (newMaxPage > this.totalPages) {
        return totalPages - this.pagesToDisplay + 1;
      } else {
        // Current page value is positioned in the middle of the page number list
        return this.value - pagesToAdd;
      }
    } else {
      // When the current page value is less than the default number of page buttons, return 1
      return 1;
    }
  },
  // Maximum page number in the 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 value is less than the default number of page buttons, return the number of displayed page buttons
      return this.pagesToDisplay;
    }
  }
}

Methods#

methods: {
  // Page number list array
  range(min, max) {
    let arr =[];
    for (let i = min; i <= max; i++) {
      arr.push(i);
    }
    return arr;
  },
  // Jump to 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 cannot be satisfied with just writing business components. Writing independent components like pagination has also made me think about API design and functional complexity. Regardless of how complex a component is, it generally consists of three parts: prop, event, and slot. The specific implementation mainly relies on basic JavaScript capabilities.
Previously, I also wrote radio and input components imitating Element. I hope to persist in writing these independent components and discover a larger 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.