Dismiss
Announcing Stack Overflow Documentation

We started with Q&A. Technical documentation is next, and we need your help.

Whether you're a beginner or an experienced developer, you can contribute.

Sign up and start helping → Learn more about Documentation →

I have a listing page using vue js and laravel. In this page filtering and sorting functionality is also included. Currently listing, sorting and filtering are working fine. I need to include pagination in this listing page. I don't know how to include this my client need this. I have not too much experiance in vue js side. My current code is given below. Could you please check and help me to integrate pagination?

Version are vue js(1.0.25) and laravel(5.2)

dashboard.blade.php

@extends('layouts.app')

@section('title', 'Dashboard')

@section('style')
    <style>
        th.active .arrow {
            opacity: 1;
        }

        .arrow {
            display: inline-block;
            vertical-align: middle;
            width: 0;
            height: 0;
            margin-left: 5px;
            opacity: 0.66;
        }

        .arrow.asc {
            border-left: 4px solid transparent;
            border-right: 4px solid transparent;
            border-bottom: 4px solid #42b983;
        }

        .arrow.dsc {
            border-left: 4px solid transparent;
            border-right: 4px solid transparent;
            border-top: 4px solid #42b983;
        }

        #search {
            margin-bottom: 10px;
        }
    </style>
@endsection

@section('content')
    <div class="container">
        <div class="row">
            <h1 class="page-header">{{ trans('messages.customerListPageHeadingLabel') }}</h1>
            <template id="grid-template">
                <table class="table table-hover table-bordered">
                    <thead>
                    <tr>
                        <th v-for="key in columns" @click="sortBy(key)" :class="{active: sortKey == key}">@{{ heading[key] }} <span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'"></span>
                        </th>
                    </tr>
                    </thead>
                    <tbody>
                    <tr v-for="(index, customer) in customers | filterBy filterKey | orderBy sortKey sortOrders[sortKey]">
                        <td>@{{ customer.erp_id }}</td>
                        <td>@{{customer.firstname}}</td>
                        <td><a href="{{ url('/customer/details/') }}/@{{ customer.id }}">@{{customer.lastname}}</a></td>
                        <td>@{{customer.email}}</td>
                        <td>@{{customer.phone_1}}</td>
                        <td>@{{customer.status}}</td>
                        <td>@{{customer.created_on}}</td>
                    </tr>
                    </tbody>
                </table>
            </template>
            <div id="app">
                <div class="form-group col-md-4">
                    <form id="search" class="form-inline">
                        <label for="query">{{ trans('messages.customerListPageSearchBox') }} </label>
                        <input name="query" class="form-control" v-model="searchQuery">
                    </form>
                </div>
                <br>
                <customer-grid  :customers="{{$listCustomers}}"  :columns="gridColumns" :heading="colTitles"  :filter-key="searchQuery"></customer-grid>
            </div>
        </div>
    </div>
    @endsection

    @push('script')
            <!-- Vue Js -->
    <script src="/assets/js/vue.js"></script>
    <script src="/assets/js/vue-resource.js"></script>
    <script>
        Vue.component('customer-grid', {
            template: '#grid-template',
            props: {
                customers: Array,
                columns: Array,
                filterKey: String,
                heading:Object
            },

            data: function () {
                var sortOrders = {}
                this.columns.forEach(function (key) {
                    sortOrders[key] = 1
                })
                return {
                    sortKey: '',
                    sortOrders: sortOrders
                }
            },
            methods: {
                sortBy: function (key) {
                    this.sortKey = key
                    this.sortOrders[key] = this.sortOrders[key] * -1
                }
            }
        })

        // bootstrap the demo
        var demo = new Vue({
            el: '#app',
            data: {
                searchQuery: '',
                gridColumns: ['erp_id', 'firstname', 'lastname', 'email', 'phone_1', 'status', 'created_on'],
                gridData: null,
                colTitles: {'erp_id':'@lang('messages.customerListPageTableCustomerNo')', 'firstname':'@lang('messages.customerListPageTableFirstname')', 'lastname':'@lang('messages.customerListPageTableLastname')', 'email':'E-Mail', 'phone_1':'@lang('messages.customerListPageTablePhone')', 'status':'Status', 'created_on':'@lang('messages.customerListPageTableAddedDate')'}
            },

            created: function() {
                this.fetchData()
            },

            methods: {
                fetchData: function () {
                    var self = this;
                    $.get('/', function( data ) {
                        self.gridData = data;
                    });
                }
            }
        });
    </script>
    @endpush

routes.php

Route::get('/', ['as' => 'listCustomersPage', 'uses' => 'CustomerController@index']);

CustomerController.php

public function index()
    {
        $listCustomers    = Customer::select('id', 'erp_id', 'firstname', 'lastname', 'email', 'phone_1', 'status', DB::raw("DATE_FORMAT(created_at, '%d.%m.%Y %H:%i') AS created_on"))
            ->orderBy('id', 'desc')
            ->get();
        return view('dashboard', compact('listCustomers'));
        /*return view('dashboard');*/
    }
share|improve this question

I've written my own pagination with vue.js (I also use Vue with laravel), because vue.js doesn't have pagination out of the box. I built this component:

<style>
    li.page-item {
        cursor: pointer;
    }
    li.active_pagination a {
        background-color: #1c84c6 !important;
        border-color: #1c84c6 !important;
        color: #ffffff !important;
    }
</style>

<template>
    <div>
        <ul class="pagination pull-right">
            <li class="page-item" :class="{'disabled': currentPage == 1}">
                <a class="page-link" href="#" @click.prevent="changeCurrentPage(1)" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                    <span class="sr-only">Previous</span>
                </a>
            </li>
            <li class="page-item" :class="{'disabled': currentPage == 1}">
                <a class="page-link" href="#" @click.prevent="decrementCurrentPage(currentPage)" aria-label="Previous">
                    <span aria-hidden="true">&lsaquo;</span>
                    <span class="sr-only">Previous</span>
                </a>
            </li>

            <li v-for="index in indexes" class="page-item" :class="{'active_pagination': currentPage == index}"><a class="page-link" @click.prevent="changeCurrentPage(index)">{{index}}</a></li>

            <li class="page-item" :class="{'disabled': currentPage == totalPages}">
                <a class="page-link" href="#" @click.prevent="incrementCurrentPage(currentPage)" aria-label="Next">
                    <span aria-hidden="true">&rsaquo;</span>
                    <span class="sr-only">Next</span>
                </a>
            </li>
            <li class="page-item" :class="{'disabled': currentPage == totalPages}">
                <a class="page-link" href="#" @click.prevent="changeCurrentPage(totalPages)" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                    <span class="sr-only">Next</span>
                </a>
            </li>
        </ul>
    </div>
</template>

<script>

    module.exports = {

        props: ['currentPage','totalPages', 'totalShownIndexes'],

        computed:  {

            indexes: function() {
                var temp_pageIndexes = [];

                if (this.totalPages > 1) {
                    if (this.totalPages <= this.totalShownIndexes) {
                        for (var i = 1; i <= this.totalPages; i++) {
                            if (temp_pageIndexes.indexOf(i) == -1) {
                                temp_pageIndexes.push(i);
                            }
                        }
                    }
                    else {
                        var sigma = Math.ceil((this.totalShownIndexes - 1) / 2);

                        if (sigma + this.currentPage >= this.totalPages) {
                            var temp_right = (this.totalPages - this.currentPage);
                            var temp_left = this.currentPage - (sigma + (sigma - temp_right));

                            temp_pageIndexes.push('...');
                            for (var i = temp_left; i <= this.totalPages; i++) {
                                temp_pageIndexes.push(i);
                            }
                        }
                        else if (this.currentPage - sigma <= 1) {
                            var temp_left = sigma - this.currentPage;
                            var temp_right = sigma + (this.currentPage + sigma + temp_left);

                            for (var i = 1; i <= temp_right - 1; i++) {
                                temp_pageIndexes.push(i);
                            }
                            temp_pageIndexes.push('...');
                        }
                        else {
                            for (var i = this.currentPage - sigma; i <= this.currentPage + sigma; i++) {
                                temp_pageIndexes.push(i);
                            }
                            temp_pageIndexes.push('...');
                        }
                    }
                    return temp_pageIndexes;
                }
                else {
                    this.currentPage = 1;
                    this.totalPages = 1;
                    return ['1'];
                }
            }
        },

        methods: {
            changeCurrentPage(newCurrentPage) {
                console.log('newCurrentPage: ' + newCurrentPage);
                if(newCurrentPage > 0 && newCurrentPage < this.totalPages + 1 && newCurrentPage != this.currentPage && newCurrentPage != '...') {
                    this.currentPage = newCurrentPage;
                    this.$parent.pagination.currentPage = this.currentPage;
                }
            },

            incrementCurrentPage(currentPage) {
                this.changeCurrentPage(Number(currentPage) + 1);
            },

            decrementCurrentPage(currentPage) {
                this.changeCurrentPage(Number(currentPage) - 1)
            }
        }
    }
</script>

I built this filter:

Vue.filter('paginate', function (list, currentPage, perPage) {

    var totalPages = Math.ceil(list.length / perPage);

    if(totalPages == 0) {
        totalPages = 1;
    }
    this.pagination.totalPages = totalPages;

    var index = (currentPage - 1) * perPage;

    return list.slice(index, index + perPage);
});

You can place this filter at the end of a filtering/sorting list like so:

v-for="(index, customer) in customers | filterBy filterKey | orderBy sortKey sortOrders[sortKey] | paginate pagination.currentPage pagination.perPage"

For this to work, you'll need a pagination object in your data object like so:

data() {
    return {
        pagination: {
            options: [200,100,50,25,10,1],
            perPage: 10,
            totalPages: 1,
            currentPage: 1,
            totalShownIndexes: 5
        }
    }
}

The options attribute is optional. I used this to fill a combobox with a perPage selection. So you have the option to choose how many items you want to show per page. On selection changed you will set the perPage attribute.

Now you're nearly done. The only thing you'll need are these methods:

changePerPage(perPage) {
    this.pagination.perPage = perPage;
    this.resetCurrentPage();
},

resetCurrentPage() {
    this.pagination.currentPage = 1;
}

The changePerPage() method is only needed if you want the combobox with per page selection.

The resetCurrentPage() method should be called whenever your other filters are applied. That is all you need to do.

I hope you will find this useful.

share|improve this answer

Pagination can be done in your eloquent select statement and all you have to do now it style its appearance on your blade edit the content of your customer controller as done below

public function index()
{
    $list = Customer::select('id', 'erp_id', 'firstname', 'lastname', 'email', 'phone_1', 'status', DB::raw("DATE_FORMAT(created_at, '%d.%m.%Y %H:%i') AS created_on"))
        ->orderBy('id', 'desc')
        ->get();
    $all = count($list);
    $listCustomers = Customer::select('id', 'erp_id', 'firstname',   'lastname', 'email', 'phone_1', 'status', DB::raw("DATE_FORMAT(created_at, '%d.%m.%Y %H:%i') AS created_on"))
        ->orderBy('id', 'desc')
        ->paginate(10);//note here that im assuming you want to display 10 results per page 
    return view('dashboard', compact('listCustomers', 'all'));
    /*return view('dashboard');*/
}

On your dashboard blade add the div that will specify the different pages

@section('title', 'Dashboard')

@section('style')
<style>
    th.active .arrow {
        opacity: 1;
    }

    .arrow {
        display: inline-block;
        vertical-align: middle;
        width: 0;
        height: 0;
        margin-left: 5px;
        opacity: 0.66;
    }

    .arrow.asc {
        border-left: 4px solid transparent;
        border-right: 4px solid transparent;
        border-bottom: 4px solid #42b983;
    }

    .arrow.dsc {
        border-left: 4px solid transparent;
        border-right: 4px solid transparent;
        border-top: 4px solid #42b983;
    }

    #search {
        margin-bottom: 10px;
    }
</style>
@endsection

@section('content')
<div class="container">
    <div class="row">
        <h1 class="page-header">{{     trans('messages.customerListPageHeadingLabel') }}</h1>
        <template id="grid-template">
            <table class="table table-hover table-bordered">
                <thead>
                <tr>
                    <th v-for="key in columns" @click="sortBy(key)" :class="{active: sortKey == key}">@{{ heading[key] }} <span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'"></span>
                    </th>
                </tr>
                </thead>
                <tbody>
                <tr v-for="(index, customer) in customers | filterBy filterKey | orderBy sortKey sortOrders[sortKey]">
                    <td>@{{ customer.erp_id }}</td>
                    <td>@{{customer.firstname}}</td>
                    <td><a href="{{ url('/customer/details/') }}/@{{ customer.id }}">@{{customer.lastname}}</a></td>
                    <td>@{{customer.email}}</td>
                    <td>@{{customer.phone_1}}</td>
                    <td>@{{customer.status}}</td>
                    <td>@{{customer.created_on}}</td>
                </tr>
                </tbody>
            </table>
            <div class="w100 categoryFooter"><!--I added this-->
                 <div class="pagination pull-left no-margin-top">
                 {!! $listCustomers->links(null, ['class' => 'pagination no-margin-top']) !!}
            </div>
            <div class="pull-right pull-right col-sm-4 col-xs-12 no-padding text-right text-left-xs">
            <p>Showing 1-10 of {{ $all }} results</p>
            </div>
                        </div>
        </template>
        <div id="app">
            <div class="form-group col-md-4">
                <form id="search" class="form-inline">
                    <label for="query">{{ trans('messages.customerListPageSearchBox') }} </label>
                    <input name="query" class="form-control" v-model="searchQuery">
                </form>
            </div>
            <br>
            <customer-grid  :customers="{{$listCustomers}}"  :columns="gridColumns" :heading="colTitles"  :filter-key="searchQuery"></customer-grid>
        </div>
    </div>
</div>
@endsection

@push('script')
        <!-- Vue Js -->
<script src="/assets/js/vue.js"></script>
<script src="/assets/js/vue-resource.js"></script>
<script>
    Vue.component('customer-grid', {
        template: '#grid-template',
        props: {
            customers: Array,
            columns: Array,
            filterKey: String,
            heading:Object
        },

        data: function () {
            var sortOrders = {}
            this.columns.forEach(function (key) {
                sortOrders[key] = 1
            })
            return {
                sortKey: '',
                sortOrders: sortOrders
            }
        },
        methods: {
            sortBy: function (key) {
                this.sortKey = key
                this.sortOrders[key] = this.sortOrders[key] * -1
            }
        }
    })

    // bootstrap the demo
    var demo = new Vue({
        el: '#app',
        data: {
            searchQuery: '',
            gridColumns: ['erp_id', 'firstname', 'lastname', 'email', 'phone_1', 'status', 'created_on'],
            gridData: null,
            colTitles: {'erp_id':'@lang('messages.customerListPageTableCustomerNo')', 'firstname':'@lang('messages.customerListPageTableFirstname')', 'lastname':'@lang('messages.customerListPageTableLastname')', 'email':'E-Mail', 'phone_1':'@lang('messages.customerListPageTablePhone')', 'status':'Status', 'created_on':'@lang('messages.customerListPageTableAddedDate')'}
        },

        created: function() {
            this.fetchData()
        },

        methods: {
            fetchData: function () {
                var self = this;
                $.get('/', function( data ) {
                    self.gridData = data;
                });
            }
        }
    });
</script>
@endpush

Make sure you change the value of 10 to a {{ $count }} variable to specify how many results are displayed per page. note it will most likely be 10 which is your maximum value except for the last page. Good luck with it

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.