A simple way of displaying dynamic pagination in Vue.js with Laravel

In any web application, pagination is a common feature being used with a table to handle a huge amount of data. This feature can help reduce the data loaded into the table by splitting it into multiples pages. Each page contains a set of data based on the number $per_page given.

Building a pagination feature from scratch can be very painful. Lucky for us, Laravel has made it easy for developers to develop this feature. As simple as adding paginate($per_page) method on a query builder, we can get data together with pagination metadata. Refer to official Laravel documentation for more details: https://laravel.com/docs/8.x/pagination

Laravel API pagination response:

// URL: http://pagination-demo.test/user?page=1

{
   "total": 567,
   "per_page": 15,
   "current_page": 1,
   "last_page": 80,
   "first_page_url": "http://pagination-demo.test/user?page=1",
   "last_page_url": "http://pagination-demo.test/user?page=80",
   "next_page_url": "http://pagination-demo.test/user?page=2",
   "prev_page_url": null,
   "path": "http://pagination-demo.test",
   "from": 1,
   "to": 15,
   "data":[
        {
            // User record...
        },
        {
            // User record...
        }
   ]
}

Now that we have the response with pagination metadata, we can use it in our Vue.js file. A simple implementation can be done by simply make a loop based on the “last_page” variable. But if the number of pages is large, for instance 50 pages, we don’t want to show each number under the table. It will look weird and hard to navigate.

<nav aria-label="Page navigation">
    <ul class="pagination">
        <li class="page-item" :class="pagination.current_page == 1 ? 'disabled' : ''"><a class="page-link" href="javascript:void(0)" @click="setPage(pagination.current_page - 1)">Previous</a></li>
        <template >
            <li class="page-item" v-for="(page, index) in pagination.last_page" :key="index" :class="pagination.current_page == page ? 'active' : ''">
                <a class="page-link" @click="setPage(page)">@{{ page }}</a>
            </li>
        <li class="page-item" :class="pagination.current_page == pagination.last_page ? 'disabled' : ''"><a class="page-link" href="javascript:void(0)" @click="setPage(pagination.current_page + 1)">Next</a></li>
    </ul>
    <small>Showing @{{ pagination.from }} to @{{ pagination.to }} of @{{ pagination.total }} entries</small>
</nav>

Here how we can improve that. Instead of displaying each page number, we will just display the first page, the last 5 pages from the current_page, the next 5 pages from the current_page, and the last page. The tricky part is how to get the last 5 pages from the current_page and the next 5 pages from the current_page? The answer is to calculate the absolute value of page number against the current_page.

We will put these conditions (refer line no 5) to display the page number.

  • page == 1 [Show first page]
  • page == pagination.last_page [Show last page]
  • Math.abs(page – pagination.current_page) < 5 [Show last 5 pages & next 5 pages from current_page]
<nav aria-label="Page navigation">
    <ul class="pagination">
        <li class="page-item" :class="pagination.current_page == 1 ? 'disabled' : ''"><a class="page-link" href="javascript:void(0)" @click="setPage(pagination.current_page - 1)">Previous</a></li>
        <template v-for="(page, index) in pagination.last_page">
            <template v-if="page == 1 || page == pagination.last_page || Math.abs(page - pagination.current_page) < 5">
                <li class="page-item" :key="index" :class="pagination.current_page == page ? 'active' : ''">
                    <a class="page-link" @click="setPage(page)">@{{ page }}</a>
                </li>
            </template>
        </template>
        <li class="page-item" :class="pagination.current_page == pagination.last_page ? 'disabled' : ''"><a class="page-link" href="javascript:void(0)" @click="setPage(pagination.current_page + 1)">Next</a></li>
    </ul>
    <small>Showing @{{ pagination.from }} to @{{ pagination.to }} of @{{ pagination.total }} entries</small>
</nav>

Hopefully, this will help.