1
votes

I have JSON data that is already paginated from the server. I have successfully shown the items in my vuetify data table. Now I want to create pagination that loads the next or previous set of data from the server. Unable to add custom function in data table pagination that loads the next set of data from the server.

{
    "current_page": 1,
    "data": [
        {
            "id": 1,
            "contact_person": "person one",
            "email": "[email protected]",
            "phone": 1234567890,
            "gst": "0987654321",
            "user_id": 3,
            "created_at": "2019-08-17 18:30:00",
            "updated_at": "2019-08-17 18:55:32"
        },
        {
            "id": 2,
            "contact_person": "person two",
            "email": "[email protected]",
            "phone": 1234567891,
            "gst": "0987654322",
            "user_id": 3,
            "created_at": "2019-08-17 18:30:00",
            "updated_at": "2019-08-17 18:55:32"
        }
    ],
    "first_page_url": "http://localhost:8000/customer?page=1",
    "from": 1,
    "last_page": 3,
    "last_page_url": "http://localhost:8000/customer?page=3",
    "next_page_url": "http://localhost:8000/customer?page=2",
    "path": "http://localhost:8000/customer",
    "per_page": 2,
    "prev_page_url": null,
    "to": 2,
    "total": 6
}

I want "next_page_url" and "prev_page_url" to be executed on pagination icon click. Thank you in adv :)

3

3 Answers

2
votes

You need to provide the total-items property of the data-table - it will be used in conjunction with the pagination.rowsPerPage to calculate the total number of pages. Then, you will need to handle the @update:pagination event whose argument will be the new pagination object:

  pagination:
    {
      descending: !!this.$route.query.desc,
      sortBy: this.$route.query.orderby || 'name',
      rowsPerPage: +this.$route.query.limit || 10,
      page: +this.$route.query.page || 1,
      totalItems: 0,
    }

In this event handler you will fetch the specified page through AJAX and then update the items property of the data-table.

For example:

<template>
      <v-data-table 
        :headers="headers" 
        :items="domains" 
        :rows-per-page-items="listSize" 
        :pagination.sync="pagination" 
        :total-items="totalNumberOfItems" 
        @update:pagination="paginate"
      >
      ....
</template>

<script>
  data()
  {
    return {
      totalNumberOfItems: 0,
      domains: [],
      listSize: [10, 25, 50, 100],
      pagination:
        {
          descending: !!this.$route.query.desc,
          sortBy: this.$route.query.orderby || 'name',
          rowsPerPage: +this.$route.query.limit || 10,
          page: +this.$route.query.page || 1,
          totalItems: 0,
        }
    }
  },
  beforeRouteUpdate (to, from, next)
  {
    this.fetchData(to);
    next();
  },
  methods:
  {
    buildQuery (route)
    {
      const query = route ? route.query : this.$route.query;
      const paginate = this.pagination;

      let params = '?limit=' + paginate.rowsPerPage + '&orderby=' + paginate.sortBy;
      if (paginate.page > 1) params += '&start=' + (this.pagination.page - 1) * this.pagination.rowsPerPage;
      if (paginate.descending) params += '&direction=desc';

      return params;
    },
    fetchData (route)
    {
      this.$ajax(
        {
          method: 'GET',
          url: '/getData/' + this.buildQuery(route),
          okay: (data) =>
          {
            this.totalNumberOfItems = data.resulttotals;
            this.domains = data.items;
          },
          fail: (stat, error) =>
          {
            this.$root.showFailed(error);
          },
        }
      );
    },
      paginate (val)
      {
        // emitted by the data-table when changing page, rows per page, or the sorted column/direction - will be also immediately emitted after the component was created
        const query = this.$route.query;
        const obj = Object.assign({}, query);
        if (val.rowsPerPage !== this.listSize[0]) obj.limit = val.rowsPerPage;
        if (val.descending) obj.desc = 'true';
        else delete obj.desc;
        obj.orderby = val.sortBy;
        obj.page = val.page;
        // check if old and new query are the same - VueRouter will not change the route if they are, so probably this is the first page loading
        let same = true;
        for (let key in query)
        {
          if (query[key] != obj[key])
          {
            same = false;
            break;
          }
        }
        // to handle the case when a KEY exists in OBJ but not in query
        for (let key in obj)
        {
          if (query[key] != obj[key])
          {
            same = false;
            break;
          }
        }
        if (same) this.fetchData(); // page has been manually reloaded in the browser
        else
        {
          this.$router.replace({
            ...this.$router.currentRoute,
            query: obj
          });
        }
      },
  }
</script>
1
votes

just listen for the @pagination event on the v-data-table .. so like <v-data-table @pagination="myFunction" > which will give

{
  page: number,
  itemsPerPage: number,
  pageStart: number,
  pageStop: number,
  pageCount: number,
  itemsLength: number
}

that as the parameter of the function called by the pagination event. if the page number or items per page change, just make a new request with the new page number / items per page.

v-data-table docs

-1
votes

refer: https://vuetifyjs.com/en/components/data-tables/#server-side-paginate-and-sort

If you’re loading data already paginated and sorted from a backend, you can use the server-items-length prop. Defining this prop will disable the built-in sorting and pagination, and you will instead need to use the available events (update:page, update:sortBy, update:options, etc) to know when to request new pages from your backend. Use the loading prop to display a progress bar while fetching data.

<script>
  export default {
    data () {
      return {
        totalDesserts: 0,
        desserts: [],
        loading: true,
        options: {},
        headers: [
          {
            text: 'Dessert (100g serving)',
            align: 'start',
            sortable: false,
            value: 'name',
          },
          { text: 'Calories', value: 'calories' },
          { text: 'Fat (g)', value: 'fat' },
          { text: 'Carbs (g)', value: 'carbs' },
          { text: 'Protein (g)', value: 'protein' },
          { text: 'Iron (%)', value: 'iron' },
        ],
      }
    },
    watch: {
      options: {
        handler () {
          this.getDataFromApi()
        },
        deep: true,
      },
    },
    mounted () {
      this.getDataFromApi()
    },
    methods: {
      getDataFromApi () {
        this.loading = true
        this.fakeApiCall().then(data => {
          this.desserts = data.items
          this.totalDesserts = data.total
          this.loading = false
        })
      },
      /**
       * In a real application this would be a call to fetch() or axios.get()
       */
      fakeApiCall () {
        return new Promise((resolve, reject) => {
          const { sortBy, sortDesc, page, itemsPerPage } = this.options

          let items = this.getDesserts()
          const total = items.length

          if (sortBy.length === 1 && sortDesc.length === 1) {
            items = items.sort((a, b) => {
              const sortA = a[sortBy[0]]
              const sortB = b[sortBy[0]]

              if (sortDesc[0]) {
                if (sortA < sortB) return 1
                if (sortA > sortB) return -1
                return 0
              } else {
                if (sortA < sortB) return -1
                if (sortA > sortB) return 1
                return 0
              }
            })
          }

          if (itemsPerPage > 0) {
            items = items.slice((page - 1) * itemsPerPage, page * itemsPerPage)
          }

          setTimeout(() => {
            resolve({
              items,
              total,
            })
          }, 1000)
        })
      },
      getDesserts () {
        return [
          {
            name: 'Frozen Yogurt',
            calories: 159,
            fat: 6.0,
            carbs: 24,
            protein: 4.0,
            iron: '1%',
          },
          {
            name: 'Ice cream sandwich',
            calories: 237,
            fat: 9.0,
            carbs: 37,
            protein: 4.3,
            iron: '1%',
          },
          {
            name: 'Eclair',
            calories: 262,
            fat: 16.0,
            carbs: 23,
            protein: 6.0,
            iron: '7%',
          },
          {
            name: 'Cupcake',
            calories: 305,
            fat: 3.7,
            carbs: 67,
            protein: 4.3,
            iron: '8%',
          },
          {
            name: 'Gingerbread',
            calories: 356,
            fat: 16.0,
            carbs: 49,
            protein: 3.9,
            iron: '16%',
          },
          {
            name: 'Jelly bean',
            calories: 375,
            fat: 0.0,
            carbs: 94,
            protein: 0.0,
            iron: '0%',
          },
          {
            name: 'Lollipop',
            calories: 392,
            fat: 0.2,
            carbs: 98,
            protein: 0,
            iron: '2%',
          },
          {
            name: 'Honeycomb',
            calories: 408,
            fat: 3.2,
            carbs: 87,
            protein: 6.5,
            iron: '45%',
          },
          {
            name: 'Donut',
            calories: 452,
            fat: 25.0,
            carbs: 51,
            protein: 4.9,
            iron: '22%',
          },
          {
            name: 'KitKat',
            calories: 518,
            fat: 26.0,
            carbs: 65,
            protein: 7,
            iron: '6%',
          },
        ]
      },
    },
  }
</script>
<template>
  <div>
    <v-data-table
      :headers="headers"
      :items="desserts"
      :options.sync="options"
      :server-items-length="totalDesserts"
      :loading="loading"
      class="elevation-1"
    ></v-data-table>
  </div>
</template>