3
votes

I have an working pagination,using (Laravel,Vuejs and Bootstrap-Vue), but I need to add the page number in url to use history. (for back button). That's what I have till now. The goal is place page nr. in url,to have a back button.

{
    path: "/",  //here I will change it with "/:id"
    name: "Home",
    component: Home,
},

<b-pagination
    v-model="currentPage"
    :per-page="perPage"
    :total-rows="totalRows"
>
</b-pagination>   //this is my pagination nav, that takes currentPage from get Request

axios.get('/list',{
    params: {
        'page': this.currentPage,
        'per_page': this.perPage,
    },
})
.then(response => {
    this.perPage = response.data.per_page;
    this.totalRows = response.data.total;
})
.catch(function (error) {
    console.log('Error');
})  //and this is the get request

Update

I add router.push({ path: "/", query: { page: this.currentPage } }); on my get response. I have the path ,but when I try to access page 2 , the id it'changed in 2, and in 1 again. and I got an error of duplicate.

NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated", message: "Navigating to current location ("/?page=1") is not allowed"

UPDATE 2 I almost made it, the only thing that don't work yet is active class, on pagination, always page 1 is active. (content,url and currentPage variable are changed)

watch:{
    currentPage() {
      this.$router
        .push({
          query: {
            ...this.$route.query,
            page: this.currentPage
          }
        })
        .catch(() => {});
    },
}

//In reseponse from axios:
this.currentPage = response.data.current_page;
2

2 Answers

1
votes

Based on this answer on how to replace the current query with another, and this answer on how you can simply ignore the error, i came up with the solution below.

I use a computed property to automatically change the URL when our current page changes, and based on the answer i add an empty .catch to our push to suppress the error, since it still navigates just fine.

Edit

It completely forgot about the b-pagination-nav component, which is designed to change the URL. I think the first example in the documentation might be of use to you, since that changes the current page with a ?page=n query.

<template>
  <b-pagination
    v-model="currentPage"
    :per-page="perPage"
    :total-rows="totalRows"
  >
  </b-pagination>
  <b-table :items="items" :current-page="currentPage" :per-page="perPage">
  </b-table>
</template>

<script>
export default {
  created() {
    // Save the page for later use.
    // If our query isn't found, we default to page 1.
    const page = this.$route.query.page || 1;

    // fetch our data, this fetches all records and uses clientside pagination.
    fetch("https://example.com/movies.json")
      .then(resp => resp.json())
      .then(data => {
        this.items = data;

        // since b-pagination will change the currentPage to 1,
        // we need to change it back to the actual page after fetching our items.
        this.$nextTick(() => {
          this.currentPage = page;
        });
      });
  },
  computed: {
    totalRows() {
      return this.items.length;
    },
    currentPage: {
      get() {
        return this.$route.query.page || 1;
      },
      set(newPage) {
        // You could alternatively call your API here if you have serverside pagination

        this.$router
          .push({ query: { ...this.$route.query, page: newPage } })
          .catch(() => {});
      }
    }
  },
  data() {
    return {
      perPage: 5,
      items: []
    };
  }
};
</script>

0
votes

I would suggest to use <b-pagination-nav> instead of <b-pagination> since the second has issues when you try to use it to change the url afterwards and work with browser history (back/forward buttons). If you don't care about browser history you could also check this answer Getting bootstrap vue pagination to play with REST api

Here is my data:

 data() {
        return {
            totalRows: 0, // make sure to update that when calling the server
            totalPages: 1, // this too
            isBusy: false,
            FPS: {
                filter: {},
                currentPage: 1
            }
        }
    },

Here is my <b-table>

<b-table
    :items="dataProvider"
    :fields="fields"
    :per-page="FPS.perPage"
    :filter="FPS.filter"
    :sort-by="FPS.sortBy"
    :sort-desc="FPS.sortDesc"
    :head-variant="headVariant"
    :current-page="FPS.currentPage"
    :busy="isBusy"
    :api-url="apiUrl"
    responsive
    bordered
    hover
>

and here is my pagination component

<b-pagination-nav
    v-model="FPS.currentPage"
    align="right"
    use-router
    :number-of-pages="Math.max(1,totalPages)"
    :link-gen="pageChanged"
/>

add this watcher to track all the $route.query changes (this works also on the initial reload)

    watch: {
        '$route.query': {
            handler(newVal) {
                const currentPageFromUrl = parseInt(newVal.currentPage)
                this.FPS = this.sanitizeQueryParams(newVal)
                this.$nextTick(() => {
                    if (currentPageFromUrl) {
                        this.FPS.currentPage = currentPageFromUrl
                    }
                })
            },
            immediate: true
        }
    },

then add this method (even though this changes the query part it still works with browser back/forward buttons!):

pageChanged(pageNum) {
    return {
        path: this.$route.path,
        query: { ...this.$route.query, currentPage: pageNum }
    }
},

and this should do it. I haven't tested this separately but it works with conjunction to my @filtered, @sort-changed events that are on b-table

for context here is my sanitizeQueryParams (essentially converting strings to appropriate types and filling the blanks when needed)

sanitizeQueryParams(queryParams) {
    const result = {}
    if (!queryParams) {
        queryParams = {}
    }
    if (queryParams.filter && typeof queryParams.filter === 'string') {
        result.filter = JSON.parse(queryParams.filter)
    } else {
        result.filter = queryParams.filter || {}
    }
    if (!queryParams.sortBy) {
        result.sortBy = 'id'
    } else {
        result.sortBy = queryParams.sortBy
    }
    if (queryParams.sortDesc === undefined) {
        result.sortDesc = true
    }
    if (queryParams.sortDesc === 'true') {
        result.sortDesc = true
    } else if (queryParams.sortDesc === 'false') {
        result.sortDesc = false
    } else {
        result.sortDesc = queryParams.sortDesc
    }
    if (!queryParams.perPage) {
        result.perPage = 10
    } else {
        result.perPage = parseInt(queryParams.perPage)
    }
    if (!queryParams.currentPage) {
        result.currentPage = 1
    } else {
        result.currentPage = parseInt(queryParams.currentPage)
    }
    return result
}