vue-pagination-axios-api-bootstrap-vue-feature-image

Vue Pagination with Axios and API (Server Side pagination) example

In this tutorial, I will show you how to make Pagination in a Vue.js Application with existing API (Server Side pagination) using Axios and Bootstrap-Vue.

Related Posts:
Vue.js 2 CRUD Application with Vue Router & Axios
Vue.js JWT Authentication with Vuex and Vue Router
Vue File Upload example using Axios


Vue Pagination with API overview

One of the most important things to make a website friendly is the response time, and pagination comes for this reason. For example, this bezkoder.com website has hundreds of tutorials, and we don’t want to see all of them at once. Paging means displaying a small number of all, by a page.

Assume that we have tutorials table in database like this:

vue-pagination-axios-api-bootstrap-vue-db-table

Our Vue.js app will display the result with pagination:

vue-pagination-axios-api-bootstrap-vue-default-paging

Change to a page with larger index:

vue-pagination-axios-api-bootstrap-vue-page-change

We can change quantity of items per page:

vue-pagination-axios-api-bootstrap-vue-items-per-page-change

Or paging with filter:

vue-pagination-axios-api-bootstrap-vue-paging-with-filter

The API for this Vue client can be found at one of following posts:
Node.js Express Pagination with Sequelize and MySQL
Node.js Express Pagination with MongoDB & Mongoose
Spring Boot Pagination & Filter example | Spring JPA, Pageable

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. Bootstrap-Vue Pagination component supports this scenario, so we actually only need to use tutorials and totalItems when working with this library.

Bootstrap-Vue Pagination component

Bootstrap-Vue provides Pagination component (b-pagination) that enables the user to select a specific page from a range of pages.

This is a basic Pagination component:

vue-pagination-axios-api-bootstrap-vue-default

<b-pagination
  v-model="currentPage"
  :total-rows="rows"
  :per-page="perPage"
></b-pagination>
  • v-model: bind the current page number value
  • total-rows: total items
  • per-page: number of items per page

Notice that page numbers are indexed from 1, the number of pages is computed from total-rows and per-page.

You can also customize the appearance of Pagination component with content/size of the buttons, pill style like following samples:

vue-pagination-axios-api-bootstrap-vue-customize-pagination-component

The corresponding code will be:

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  size="sm"
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  size="lg"
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  pills
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  first-number
  last-number
></b-pagination>

<b-pagination
  v-model="page"
  :total-rows="count"
  :per-page="pageSize"
  first-text="First"
  prev-text="Prev"
  next-text="Next"
  last-text="Last"
></b-pagination>

For more details, please visit:
BootstrapVue Pagination Component Reference

For handling page changes, we use change event:

<template>
  <div>
      <b-pagination
        @change="handlePageChange"
      ></b-pagination>
      ...
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      page: 1,
    };
  },
  methods: {
    handlePageChange(value) {
      this.page = value;
    },
  },
};
</script>

handlePageChange will be invoked whenever the page changes via user interaction (click), value is the selected page number.

Technology

  • Vue 2.6
  • axios 0.19.2
  • bootstrap 4.5.0
  • bootstrap-vue 2.16.0

Setup Vue.js Application

Open cmd at the folder you want to save Project folder, run command:
vue create vue-js-client-crud

You will see some options, choose default (babel, eslint).
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

services

TutorialDataService.js

App.vue

main.js

package.json


You can follow step by step, or get source code in this post:
Vue.js 2 CRUD Application with Vue Router & Axios

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.

vue-pagination-axios-api-bootstrap-vue-project-structure

Or you can get the new Github source code at the end of this tutorial.

Setup Bootstrap Vue for Vue Pagination App

We need to install both Bootstrap core and Bootstrap Vue with command:
npm install bootstrap-vue bootstrap

Register BootstrapVue in your app entry point:

src/main.js

import Vue from 'vue'
import App from './App.vue'
import { BootstrapVue } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

...
Vue.use(BootstrapVue)

new Vue({
  render: h => h(App),
}).$mount('#app')

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.

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 Pagination

This component has:

  • a search bar for finding Tutorials by title.
  • a select element for quantity of items per page.
  • a Bootstrap-Vue Pagination component
  • a tutorials array displayed as a list on the left.
  • a selected Tutorial which is shown on the right.

vue-pagination-axios-api-bootstrap-vue-default-paging

components/TutorialsList.vue

<template>
  <div class="list row">
    <div class="col-md-8">
      <div class="input-group mb-3">
        <input
          type="text"
          class="form-control"
          placeholder="Search by title"
          v-model="searchTitle"
        />
        <div class="input-group-append">
          <button
            class="btn btn-outline-secondary"
            type="button"
            @click="page = 1; retrieveTutorials();"
          >
            Search
          </button>
        </div>
      </div>
    </div>

    <div class="col-md-12">
      <div class="mb-3">
        Items per Page:
        <select v-model="pageSize" @change="handlePageSizeChange($event)">
          <option v-for="size in pageSizes" :key="size" :value="size">
            {{ size }}
          </option>
        </select>
      </div>

      <b-pagination
        v-model="page"
        :total-rows="count"
        :per-page="pageSize"
        prev-text="Prev"
        next-text="Next"
        @change="handlePageChange"
      ></b-pagination>
    </div>

    <div class="col-md-6">
      <h4>Tutorials List</h4>
      <ul class="list-group" id="tutorials-list">
        <li
          class="list-group-item"
          :class="{ active: index == currentIndex }"
          v-for="(tutorial, index) in tutorials"
          :key="index"
          @click="setActiveTutorial(tutorial, index)"
        >
          {{ tutorial.title }}
        </li>
      </ul>
    </div>

    ...
  </div>
</template>

<script>
export default {
  ...
}
</script>

We will have following properties in data:
– search and display Tutorials:

  • searchTitle
  • tutorials
  • currentTutorial and currentIndex

– pagination:

  • page: current page
  • count: 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: [],
      currentTutorial: null,
      currentIndex: -1,
      searchTitle: "",

      page: 1,
      count: 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, totalItems } = response.data;
          this.tutorials = tutorials;
          this.count = totalItems;

          console.log(response.data);
        })
        .catch((e) => {
          console.log(e);
        });
    },

    handlePageChange(value) {
      this.page = value;
      this.retrieveTutorials();
    },

    handlePageSizeChange(event) {
      this.pageSize = event.target.value;
      this.page = 1;
      this.retrieveTutorials();
    },

    ...
  }
};
</script>

Let me explain some lines of code.

In the retrieveTutorials() method:
– We get searchTitle, page, pageSize value and transform them into params object:

{
    "title": searchTitle,
    "page": page - 1,
    "size": pageSize
}

– We use tutorials and totalItems as count value from the response data:

{
    "totalItems": 8,
    "tutorials": [...],
    "totalPages": 3,
    "currentPage": 1
}

handlePageChange() and handlePageSizeChange() methods are for setting new page and pageSize and then invokes retrieveTutorials() that updates the tutorials List after pagination information changes.

Configure Port for Vue 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, create vue.config.js file with following content:

module.exports = {
  devServer: {
    port: 8081
  }
}

Run Vue 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 Vue Pagination example that use Axios to consume API (server-side pagination) successfully with Bootstrap Vue for Pagination UI. I hope you apply it in your project at ease.

Happy learning, see you again!

Further Reading

Source Code

You can find the complete source code for this tutorial on Github.

Leave a Reply

Your email address will not be published. Required fields are marked *