Complete guide on implementing Server-Side Search with Vue Good Table

Vue-good-table's in-built features like sorting, paging, filtering etc. are all performed client side and therefore are great for most of the use-cases. Sometimes though, we might have too much data to render all of it at once on the UI. In such cases, we would want to do things like sorting, paging, filtering on the server side.

In this tutorial, we’ll be implementing server side search functionality in Vue Good table using Laravel API on the backend with VueJS on frontend.

Please note that, you must have at least some basic understandings of Laravel & VueJS to get along.

There’s a new section in Vue Good Table’s documentation about setting up Server side Search with remote mode, which i personally found quite helpful.

What’ll be doing?

We’ll be getting users records from the Database and pass it over to Vue-Good-table and perform server side searching.

Project Setup

Follow along the steps for a complete guide on implementing server-side search with Laravel and Vue-Good-Table.

Create a new Laravel Project

Head over to your projects folder, in my case its ~/code and run the following command.

laravel new vue-good-table-search-example

Setting up Database

Here we’ll perform the basic operations like setting up the DB and other configurations.

Create a new database and assign values in .env. In our case, its vuesearch

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vuesearch
DB_USERNAME=root
DB_PASSWORD=root

Now before running our default migrations, let’s create a simple Users Table Seeder.

php artisan make:seeder UsersTableSeeder

And now in UsersTableSeeder.php

/**
Run the database seeds.
*
@return void
*/
public function run() {
factory( User::class, 20 )->create();
}

Now run the migration with our seeded data.

php artisan migrate --seed

Setup API Endpoint

In your routes/api.php

Route::get('users','UsersController@getRecords');

Next we’ll create our controller php artisan make:controller UsersController

Now in our UsersController.php

/**
*
@param Request $request
*
*
@return \Illuminate\Http\JsonResponse
*/
public function
getRecords( Request $request ) {

$search_query = $request->searchTerm;
$users = User::where( 'name', 'LIKE', '%' . $search_query . '%' )
->get()
->toArray();
if ( $search_query ) {
$users['searchTerm'] = $search_query ?: '';
} else {
$users['searchTerm'] = $search_query ? null : '';
}

return response()->json( [
'users' => $users
] );
}

Let’s verify response on postman or on browser

Hit the URL http://www.vue-good-table-search-example.local/api/users and you shall see the JSON response data.

Now that our API is done and working fine, let’s head over to the frontend side.

Setup Frontend Routes

Now in our routes/web.php

Route::get('/', function () {
return view('users');
});

Now create a users blade template, in our case, we just renamed welcome.blade.php to users.blade.php with minor modifications.

Now inside of body tags of users.blade.php

<div id="app">
<div class="flex-center position-ref full-height">

<div class="content">
<vue-search
endpoint={{config('app.url')}}api/users
></vue-search>
</div>
</div>
</div>
<script src="{{ asset('js/app.js') }}"></script>

We’ll create our Vue Component in a while. For ease I’m used to pass API endpoint as prop to our component.

You may also define your application endpoint in config/app.php

Setup Vue Component

Rename ExampleComponent.vue to VueSearchComponent.vue

oh wait, we forgot to run npm install to get our frontend dependencies in place.

Run the command and add link in our users blade template.

Install Vue-good-table

If you’re using Vue-good-table for the first time, head over to their documentation to learn more about it.

Now run npm install vue-good-table and later import in our VueSearchComponent.

Now in our VueSearchComponent.vue

<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Users</div>

<div class="card-body">
<vue-good-table
mode="remote"
:columns="columns"
:rows="rows"
:globalSearch="true"
:search-options="{
enabled: true,
skipDiacritics: true,
}"
@on-search="onSearch"
styleClass="table table-hover table-bordered table-responsive">
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field === 'S.No'">

{{ (users.per_page * (users.current_page - 1)) + (props.index + 1)
}}
</span>
<span v-else>
{{props.formattedRow[props.column.field]}}
</span>
</template>
</vue-good-table>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import Vue from 'vue';
import VueGoodTable from 'vue-good-table';
import
'vue-good-table/dist/vue-good-table.css'

Vue.use(VueGoodTable);
export default {
props: ['endpoint'],
data() {
return {
users: {
searchTerm: '',
total: 0,
per_page: 5,
from: 1,
to: 0,
current_page: 1
},
offset: 4,
columns: [
{
label: 'S.No',
field: 'S.No'
},
{
label: 'User Name',
field: 'name',
filterable: true
},
{
label: 'Email Address',
field: 'email',
filterable: true
},
{
label: 'Created At',
field: 'created_at',
filterable: true
},
],
rows: []
}
},
mounted() {
this.getRecords()
},
methods: {

getRecords() {
return axios.get(`${this.endpoint}?searchTerm=${this.users.searchTerm}`).then((response) => {
this.rows = response.data.users.data
this.users = response.data.users
})
},
updateParams(newProps) {
this.users = Object.assign({}, this.users, newProps);
},
onSearch: _.debounce(function (params) {
this.updateParams(params);
this.getRecords();
return false;
}, 500)
}
}
</script>

Now run npm run dev to build our assets and head over to your browser to see the server side searching in action.

Bonus Section: How about adding Pagination now?

To setup pagination, let’s make few changes to our API. Back to our UsersController.php

$search_query = $request->searchTerm;
$perPage = $request->per_page;
$users = User::where( 'name', 'LIKE', '%' . $search_query . '%' )
->paginate( $perPage )
->toArray();

We’ll create a separate Paginator.vue component for our server-side Pagination and include it into our main VueSearchComponent.

Create a new Paginator component and add it in our app.js

Vue.component('pagination', require('./components/Paginator'));

Now in Paginator.vue

<template>
<ul class="pagination">
<li v-if="pagination.current_page > 1" class="page-item" >
<a href="javascript:void(0)" aria-label="Previous" v-on:click.prevent="changePage(pagination.current_page - 1)">
<span aria-hidden="true">«</span>
</a>
</li>
<li v-for="page in pagesNumber" :class="{'active': page == pagination.current_page}">
<a href="javascript:void(0)" v-on:click.prevent="changePage(page)">{{ page }}</a>
</li>
<li v-if="pagination.current_page < pagination.last_page">
<a href="javascript:void(0)" aria-label="Next" v-on:click.prevent="changePage(pagination.current_page + 1)">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</template>
<script>
export default{
props: {
pagination: {
type: Object,
required: true
},
offset: {
type: Number,
default: 4
}
},
computed: {
pagesNumber() {
if (!this.pagination.to) {
return [];
}
let from = this.pagination.current_page - this.offset;
if (from < 1) {
from = 1;
}
let to = from + (this.offset * 2);
if (to >= this.pagination.last_page) {
to = this.pagination.last_page;
}
let pagesArray = [];
for (let page = from; page <= to; page++) {
pagesArray.push(page);
}
return pagesArray;
}
},
methods : {
changePage(page) {
this.pagination.current_page = page;
this.$emit('paginate');
}
}
}
</script>
<style>
.pagination > .active > a, .pagination > .active > a:focus{
border-color:#004590;
background-color:red;
}
.pagination {
list-style: none;
display: inline-flex
}
</style>

Now we’ll make slight changes to our VueSearchComponent for pagination.

<vue-pagination :pagination="users"
@paginate="getRecords()"
:offset="4">
</vue-pagination>

Now for our axios call, we’ll have to pass the page and per_page attributes likeso,

getRecords() {
return axios.get(`${this.endpoint}?searchTerm=${this.users.searchTerm}&page=${this.users.current_page}&per_page=${this.users.per_page}`).then((response) => {
this.rows = response.data.users.data
this.users = response.data.users
})
},

Also, don’t forget to import the Paginator component to your VueSearchComponent.

import VuePagination from './Paginator';

and

components: {
VuePagination,
},

Now for the final time, run npm run dev and head back to your browser to see server-side pagination and search in action. Bam!

Conclusion

We’ve successfully implemented Server-side Search and Pagination with Vue-good-table and Laravel. If you’ve any queries or feedbacks, do leave us a comment below.

For complete application setup, you may check out the Github Repo for Vue Server Side Search Example.