Getting bootstrap vue pagination to play with REST api

minisaurus picture minisaurus · Feb 2, 2019 · Viewed 15.5k times · Source

Trying to get Bootstrap Vue to play with a REST api that returns data for one page and the total number of records (based on this):

<template>
  </div>
    <b-pagination 
      v-on:change="onPageChange" 
      :total-rows="totalRows" 
      :per-page="perPage" 
      v-model="currentPage"  />
    <b-table responsive striped hover show-empty
      stacked="md"
      :items="items"
      :fields="fields"
      :current-page="currentPage"
      :per-page="perPage"
      :filter="filter"
      :sort-by.sync="sortBy"
      :sort-desc.sync="sortDesc"
      :sort-direction="sortDirection"
      @filtered="onFiltered">
    </b-table>
  </div>
</template>
<script>
...
export default {
  name: 'TableList',
  data() {
    return {
      module: null,
      title: 'Table',
      items: [],
      fields: [],
      errors: [],
      currentPage: 1,
      perPage: 15,
      totalRows: 0,
      pageOptions: [ 5, 10, 15 ],
      sortBy: null,
      sortDesc: false,
      sortDirection: 'asc',
      filter: null,
    }
  },
  created() {
    ...
    this.fetch();
  },
  methods: {
    fetch() {
      var me = this;
      var requestParams = {
        page: this.currentPage,
        per_page: this.perPage
      };
      if(this.sortBy) {
        requestParams = Object.assign({ sort_by: this.sortBy }, requestParams);
      }
      Rest('GET', '/table/' + this.table, requestParams, this.$root.user.token)
      .then(response => {
        me.items = response.data[1]
        me.totalRows = response.data[0].total_entries
      })
      .catch(error => {
        this.errors.push('Error: ' + error.response.status + ': ' + error.response.statusText)
      })
      .finally(() => {
        //alert('turn off loader!');
      });
    }
  }
</script>

This Vue works if I fetch the entire table. However, when I use the REST api to return one page at a time, the number of pages is calculated to be 1, and the forward and end links are inactive. Thus, I am unable to trigger a request for e.g. page 2.

The REST api correctly returns the total number of rows in the table, and the number of rows requested, but Bootstrap Vue does not appear to be watching/reacting to changes to this.totalRows.

What have I missed?

Answer

DigitalDrifter picture DigitalDrifter · Feb 2, 2019

You need to set the per-page prop to 0 on the b-table component to disable the local pagination and allow b-pagination to handle the data. Here's an example:

new Vue({
  el: '#app',
  data() {
    return {
      items: [],
      fields: [{
          key: 'postId',
          label: 'Post ID'
        },
        {
          key: 'id',
          label: 'ID'
        },
        {
          key: 'name',
          label: 'Name'
        },
        {
          key: 'email',
          label: 'Email'
        },
        {
          key: 'body',
          label: 'Body'
        }
      ],
      currentPage: 0,
      perPage: 10,
      totalItems: 0
    }
  },
  mounted() {
    this.fetchData().catch(error => {
      console.error(error)
    })
  },
  methods: {
    async fetchData() {
      this.items = await fetch(`https://jsonplaceholder.typicode.com/comments?_page=${this.currentPage}&_limit=${this.perPage}`)
        .then(res => {
          this.totalItems = parseInt(res.headers.get('x-total-count'), 10)

          return res.json()
        })
        .then(items => items)
    }
  },
  watch: {
    currentPage: {
      handler: function(value) {
        this.fetchData().catch(error => {
          console.error(error)
        })
      }
    }
  }
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.js"></script>
<script src="//unpkg.com/babel-polyfill@latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.js"></script>

<div id="app">
  <b-table show-empty :items="items" :fields="fields" :current-page="currentPage" :per-page="0"></b-table>
  <b-pagination size="md" :total-rows="totalItems" v-model="currentPage" :per-page="perPage"></b-pagination>
</div>