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#
- The number of displayed page buttons for the pagination component can be defined using the
pagesToDisplay()
computed property - The color theme can be set by binding a
paginationClass
class that returns the desired colortype
- 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.