Pagination Component using Vue 2.0
Oct 30, 2016
Pagination is one of the basic component we will need with any application. Let’s create a pagination component using Bootstrap and Vue.js, and in the course of development we will see about custom events in Vue.js.
As I always suggest, let’s decide how the component look from outside by defining what are the properties and events we will need.
<!--Method 1 -->
<pagination :current-page="currentPage"
:total-pages="totalPages"
@page-changed="handleIt">
</pagination>
<!--Method 2 -->
<pagination :current-page="currentPage"
:total-items="totalItems"
:items-per-page="itemsPerPage"
@page-changed="handleIt">
</pagination>
Note we are going to use Vue.js 2.0.
We are going to have two different interfaces for different scenarios. This covers two possible scenarios:
- You know total number of pages you need
- You know total number of items and items per page
So the properties have to be defined for the component accordingly
export default {
props: {
// Current Page
currentPage: {
type: Number,
required: true
},
// Total number of pages
totalPages: Number,
// Items per page
itemsPerPage: Number,
// Total items
totalItems: Number,
// Visible Pages
visiblePages: {
type: Number,
default: 5,
coerce: (val) => parseInt(val)
}
}
/*
More to come
*/
}
visiblePages
is an optional property which defines how many pages should be
displayed in the pagination component. It will be
Let’s start building the component by defining the template first.
<template>
<ul class="pagination">
<li>
<a href="#" @click.prevent="pageChanged(1)" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li v-for="n in paginationRange" :class="activePage(n)">
<a href="#" @click.prevent="pageChanged(n)">{{ n }}</a>
</li>
<li>
<a href="#" @click.prevent="pageChanged(lastPage)" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</template>
The above template will display a simple Bootstrap styled Pagination.
The template is a lot expressive and self-explanatory. We have First Page & Last Page arrows
at two ends and a set of pages in the middle with corresponding events attached to it.
paginationRange
is a computed property which helps us limit the number of pages that should be displayed when we have two many pages.
Time to code the two computed properties which we will need to make this component:
lastPage
and paginationRange
.
lastPage
has to be calculated based on the inputs given. If the totalPages
property is given then it has to be taken as the lastPage, otherwise lastPage
has to be calculated
from itemsPerPage
& totalItems
.
computed: {
lastPage () {
if (this.totalPages) {
return this.totalPages
} else {
return this.totalItems % this.itemsPerPage === 0
? this.totalItems / this.itemsPerPage
: Math.floor(this.totalItems / this.itemsPerPage) + 1
}
}
paginationRange () {
/**
* Code for pagination range here
*/
}
}
When there are 100 pages, we don’t want to display all 100 pages. Only a set of pages around the
current selected page is enough to be displayed. paginationRange
computed property will take care of
that with a little bit of logic and math.
paginationRange () {
let start =
this.currentPage - this.visiblePages / 2 <= 0
? 1 : this.currentPage + this.visiblePages / 2 > this.lastPage
? Util.lowerBound(this.lastPage - this.visiblePages + 1, 1)
: Math.ceil(this.currentPage - this.visiblePages / 2)
let range = []
for (let i = 0; i < this.visiblePages && i < this.lastPage; i++) {
range.push(start + i)
}
return range
}
Here Util
is a custom utility object. It’s a good practice to place any logic which can be useful
somewhere else into a separate class or object for better reusabilty.
Now we need to highlight the active page and if you watch the template closely we have :class="activePage(n)"
which does the job. This function has to be defined within the methods
section of the component.
methods: {
activePage (pageNum) {
return this.currentPage === pageNum ? 'active' : ''
}
}
Last but the most needed part is the pageChanged
event which will trigger the handler bound
from the parent component. We are going to make a method which is bound to the click event of
the page links in the Pagination. This method will the emit the event so that the listeners
can handle it.
pageChanged (pageNum) {
this.$emit('page-changed', pageNum)
}
In case if you are trying the same with Vue 1.0, then replace $emit
with
$dispatch
.
Now let’s see how to use this in the parent component.
<template>
<div class="container">
<div id="app" class="well">
<h1>This is page {{pageOne.currentPage}}</h1>
</div>
<pagination :current-page="pageOne.currentPage"
:total-pages="pageOne.totalPages"
@page-changed="pageOneChanged">
</pagination>
</div>
</template>
<script>
import Pagination from './Pagination.vue'
export default {
components : { Pagination },
data() {
return {
pageOne: {
currentPage: 1,
totalPages: 10
}
}
},
methods: {
pageOneChanged (pageNum) {
this.pageOne.currentPage = pageNum
}
}
}
</script>
Thats it, you should have nice and good looking pagination component as shown below. You can clone and run from the Github repo.