Vuetify Pagination (Server side) example

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


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:

vuetify-pagination-server-side-example-db-table

Our Vuetify app will display the result with pagination:

vuetify-pagination-server-side-example-default-paging

Change to a page with larger index:

vuetify-pagination-server-side-example-page-change

Change quantity of items per page:

vuetify-pagination-server-side-example-page-size-change

Or paging with filter:

vuetify-pagination-server-side-example-paging-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:

vuetify-pagination-server-side-example-default

<v-pagination
  v-model="currentPage"
  :length="totalPages"
></v-pagination>
  • v-model: bind the current page number value
  • length: total pages

Notice that page numbers are indexed from 1.

You can also customize the appearance of Pagination component like following samples:

vuetify-pagination-server-side-example-custom-paging

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.

vuetify-pagination-server-side-project-structure

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.

vuetify-pagination-server-side-pagination-component

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 page
  • totalPages: total pages
  • pageSize: 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:

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

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.

2 thoughts to “Vuetify Pagination (Server side) example”

Comments are closed to reduce spam. If you have any question, please send me an email.