In this tutorial, I will show you how to make Pagination in a Vuetify App with existing API (Server Side pagination) using Axios and v-pagination.
Related Posts:
– Vuetify data-table example with a CRUD App | v-data-table
– Vuetify File Upload example
– Vue.js JWT Authentication with Vuex and Vue Router
Contents
Vuetify Pagination Server-side overview
This bezkoder.com website has hundreds of tutorials, and we don’t want to see or wait for all of them to be loaded at once. What should we do?
Pagination comes for this reason. It means displaying a small number of all, by a page.
Assume that we have tutorials table in database like this:
Our Vuetify app will display the result with pagination:
Change to a page with larger index:
Change quantity of items per page:
Or paging with filter:
The API for this Vue client can be found at one of following posts:
– Node.js Express Pagination with MySQL
– Node.js Express Pagination with PostgreSQL
– Node.js Express Pagination with MongoDB
– Spring Boot Pagination & Filter example | Spring JPA, Pageable
– Spring Boot MongoDB Pagination example with Spring Data
These Servers will exports API for pagination (with/without filter), here are some url samples:
/api/tutorials?page=1&size=5
/api/tutorials?size=5
: using default value for page/api/tutorials?page=1
: using default value for size/api/tutorials?title=data&page=1&size=3
: pagination & filter by title containing ‘data’
This is structure of the response (server-side pagination) for the HTTP GET request:
{
"totalItems": 8,
"tutorials": [...],
"totalPages": 3,
"currentPage": 1
}
This is a kind of server-side paging, where the server sends just a single page at a time. Vuetify
Pagination component supports this scenario, so we actually only need to use tutorials
and totalPages
when working with this library.
Vuetify Pagination component
Vuetify provides Pagination component (v-pagination
) that enables the user to select a specific page from a range of pages.
This is a basic Pagination component:
<v-pagination
v-model="currentPage"
:length="totalPages"
></v-pagination>
v-model
: bind the current page number valuelength
: total pages
Notice that page numbers are indexed from 1
.
You can also customize the appearance of Pagination component like following samples:
The corresponding code will be:
<v-pagination
v-model="page"
:length="totalPages"
total-visible="7"
></v-pagination>
<v-pagination
v-model="page"
:length="totalPages"
total-visible="7"
color="purple"
></v-pagination>
<v-pagination
v-model="page"
:length="totalPages"
total-visible="7"
dark
color="grey"
></v-pagination>
<v-pagination
v-model="page"
:length="totalPages"
circle
total-visible="7"
></v-pagination>
<v-pagination
v-model="page"
:length="totalPages"
circle
total-visible="7"
next-icon="mdi-menu-right"
prev-icon="mdi-menu-left"
></v-pagination>
<v-pagination
v-model="page"
:length="totalPages"
total-visible="7"
disabled
></v-pagination>
For more details, please visit:
Vuetify Pagination Component Reference
For handling page changes, we use input
event:
<template>
<div>
<v-pagination
v-model="currentPage"
:length="totalPages"
@input="handlePageChange"
></v-pagination>
...
</div>
</div>
</template>
<script>
export default {
data() {
return {
currentPage: 1,
...
};
},
methods: {
handlePageChange(value) {
this.currentPage = value;
}
}
};
</script>
handlePageChange
will be invoked whenever the page changes via user interaction (click), or value
is the selected page number.
Technology
- Vue 2.6
- axios 0.20.0
- vuetify 2.2.11
Setup Vuetify Pagination Project
Open cmd at the folder you want to save Project folder, run command:
vue create vuetify-pagination-example
You will see some options, choose default (babel, eslint).
After the Vue project is created successfully, we import Vuetify with command: vue add vuetify
.
The Console will show:
�📦 Installing vue-cli-plugin-vuetify...
+ [email protected]
added 5 packages from 7 contributors and audited 1277 packages in 25.013s
found 0 vulnerabilities
✔ Successfully installed plugin: vue-cli-plugin-vuetify
? Choose a preset: Default (recommended)
�🚀 Invoking generator for vue-cli-plugin-vuetify...
�📦 Installing additional dependencies...
added 7 packages from 5 contributors and audited 1284 packages in 41.183s
found 0 vulnerabilities
âš“ Running completion hooks...
✔ Successfully invoked generator for plugin: vue-cli-plugin-vuetify
The following files have been updated / added:
src/assets/logo.svg
src/plugins/vuetify.js
vue.config.js
package-lock.json
package.json
public/index.html
src/App.vue
src/components/HelloWorld.vue
src/main.js
Open plugins/vuetify.js, you can see:
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
Vue.use(Vuetify);
export default new Vuetify({
});
Then in main.js file, Vuetify object will added automatically to Vue App:
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
Vue.config.productionTip = false
new Vue({
vuetify,
render: h => h(App)
}).$mount('#app')
And vue.config.js:
module.exports = {
transpileDependencies: ["vuetify"]
};
After the process is done. We create new folders and files like the following tree:
public
index.html
src
components
AddTutorial.vue
Tutorial.vue
TutorialsList.vue
plugins
vuetify.js
services
TutorialDataService.js
App.vue
main.js
package.json
vue.config.js
You can follow step by step, or get source code in this post:
Vuetify data-table example with a CRUD App
The Vue Project contains structure that we only need to add some changes (in TutorialsList.vue and TutorialDataService.js) to make the pagination work well.
Or you can get the new Github source code at the end of this tutorial.
Initialize Axios
Now we’re gonna install axios with command: npm install axios
.
Then, under src folder, we create http-common.js file like this:
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8080/api",
headers: {
"Content-type": "application/json"
}
});
Remember to change the baseURL
, it depends on REST APIs url that your Server configures.
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Create Data Service
In this step, we’re gonna create a service that uses axios
object above to send HTTP requests.
services/TutorialDataService.js
import http from "../http-common";
class TutorialDataService {
getAll(params) {
return http.get("/tutorials", { params });
}
// other CRUD methods
}
export default new TutorialDataService();
In the code above, you can see that we pass params
object to GET method.
The params
object will have one, two or all fields: title
, page
, size
.
Create Vue Component with Vuetify Pagination
This component has:
- a search bar for finding Tutorials by title.
- a Vuetify Select element for quantity of items per page.
- a Vuetify Pagination component.
- a tutorials array with
v-data-table
display.
components/TutorialsList.vue
<template>
<v-row align="center" class="list px-3 mx-auto">
<v-col cols="12" sm="8">
<v-text-field v-model="searchTitle" label="Search by Title"></v-text-field>
</v-col>
<v-col cols="12" sm="4">
<v-btn small @click="page = 1; retrieveTutorials();">
Search
</v-btn>
</v-col>
<v-col cols="12" sm="12">
<v-row>
<v-col cols="4" sm="3">
<v-select
v-model="pageSize"
:items="pageSizes"
label="Items per Page"
@change="handlePageSizeChange"
></v-select>
</v-col>
<v-col cols="12" sm="9">
<v-pagination
v-model="page"
:length="totalPages"
total-visible="7"
next-icon="mdi-menu-right"
prev-icon="mdi-menu-left"
@input="handlePageChange"
></v-pagination>
</v-col>
</v-row>
</v-col>
<v-col cols="12" sm="12">
<v-card class="mx-auto" tile>
<v-card-title>Tutorials</v-card-title>
<v-data-table
:headers="headers"
:items="tutorials"
disable-pagination
:hide-default-footer="true"
>
<template v-slot:[`item.actions`]="{ item }">
<v-icon small class="mr-2" @click="editTutorial(item.id)">
mdi-pencil
</v-icon>
<v-icon small @click="deleteTutorial(item.id)">
mdi-delete
</v-icon>
</template>
</v-data-table>
...
</v-card>
</v-col>
</v-row>
</template>
<script>
export default {
...
}
</script>
We will have following properties in data
:
– search and display Tutorials:
searchTitle
tutorials
– pagination:
page
: current pagetotalPages
: total pagespageSize
: number of items in each page
For retrieving pagination data, we’re gonna use TutorialDataService.getAll()
methods.
<template>
...
</template>
<script>
import TutorialDataService from "../services/TutorialDataService";
export default {
name: "tutorials-list",
data() {
return {
tutorials: [],
searchTitle: "",
headers: [
{ text: "Title", align: "start", sortable: false, value: "title" },
{ text: "Description", value: "description", sortable: false },
{ text: "Status", value: "status", sortable: false },
{ text: "Actions", value: "actions", sortable: false },
],
page: 1,
totalPages: 0,
pageSize: 3,
pageSizes: [3, 6, 9],
};
},
methods: {
getRequestParams(searchTitle, page, pageSize) {
let params = {};
if (searchTitle) {
params["title"] = searchTitle;
}
if (page) {
params["page"] = page - 1;
}
if (pageSize) {
params["size"] = pageSize;
}
return params;
},
retrieveTutorials() {
const params = this.getRequestParams(
this.searchTitle,
this.page,
this.pageSize
);
TutorialDataService.getAll(params)
.then((response) => {
const { tutorials, totalPages } = response.data;
this.tutorials = tutorials.map(this.getDisplayTutorial);
this.totalPages = totalPages;
console.log(response.data);
})
.catch((e) => {
console.log(e);
});
},
handlePageChange(value) {
this.page = value;
this.retrieveTutorials();
},
handlePageSizeChange(size) {
this.pageSize = size;
this.page = 1;
this.retrieveTutorials();
},
getDisplayTutorial(tutorial) {
return {
id: tutorial.id,
title: tutorial.title.substr(0, 20) + "...",
description: tutorial.description.substr(0, 20) + "...",
status: tutorial.published ? "Published" : "Pending",
};
},
...
},
mounted() {
this.retrieveTutorials();
},
};
</script>
Let me explain some lines of code.
In the retrieveTutorials()
method:
– We get searchTitle
, currentPage
, pageSize
value and transform them into params
object:
{
"title": searchTitle,
"page": currentPage - 1,
"size": pageSize
}
– We use tutorials
and totalPages
from the response data:
{
"totalItems": 8,
"tutorials": [...],
"totalPages": 3,
"currentPage": 1
}
handlePageChange()
and handlePageSizeChange()
methods are for setting new currentPage
and pageSize
and then invokes retrieveTutorials()
that updates the tutorials List after pagination information changes.
Configure Port for Vuetify Pagination App
Because most of HTTP Server use CORS configuration that accepts resource sharing retricted to some sites or ports, so we also need to configure port for our App.
In project root folder, open vue.config.js file and add following config param:
module.exports = {
...
devServer: {
port: 8081
}
}
We’ve set our app running at port 8081
.
Run Vuetify Pagination App
First you need to run the Server at one of following posts:
- Node.js Express Pagination with MySQL
- Node.js Express Pagination with PostgreSQL
- Node.js Express Pagination with MongoDB
- Spring Boot Pagination & Filter example | Spring JPA, Pageable
- Spring Boot MongoDB Pagination example with Spring Data
Then you can run our App with command: npm run serve
.
If the process is successful, open Browser with Url: http://localhost:8081/
and check it.
Conclusion
Today we’ve built a Vuetify Pagination example that use Axios to consume API (server-side pagination) successfully with Vuetify Pagination Component (v-pagination
). I hope you apply it in your project at ease.
For other CRUD operations and project setup, please visit:
Vuetify data-table example with a CRUD App
Or File upload with Progress Bar:
Vuetify File Upload example
Happy learning, see you again!
Further Reading
- https://vuetifyjs.com/en/components/paginations/
- https://vuetifyjs.com/en/components/data-tables/
- https://www.npmjs.com/package/axios
For more details about ways to use Axios, please visit:
Axios request: Get/Post/Put/Delete example
Source Code
You can find the complete source code for this tutorial on Github.
Greetings! Very helpful Vuetify tutorial!
Many thanks for sharing!
NICE!
You are breathtaking, thanks…