Building your own DataTable with Laravel and VueJS from scratch
This article will show you how to use laravel and vuejs to build datatable without any plugin or third party library.
Basic Features of this Datatable:
- Real Time Search
- Real Time Sorting
- Server and Client Side Real Time Pagination
- Select Number of Rows to View
- Row Manipulation
I am very much aware that there are many packages that can be used to achieve this, the most famous in the Laravel community is Yajra/laravel-datatables, which was built with jquery. But in the tutorial you will see how you can build this with VueJS.
Step 1:
Project Setup and Database Configuration
Setup a new laravel project called Datatable or whatever you choose.
laravel new Datatableor:composer create-project --prefer-dist laravel/laravel Datatable
Change to the directory:
cd Datatable
Create a database either in your Phpmyadmin or Sequel Pro GUI.
Next edit the .env
file to have the database name, the database user and the password:
DB_DATABASE=datatable
DB_USERNAME=username
DB_PASSWORD=password
Endeavour to replace the database name, the username and the password to suit you case.
Run: php artisan config:cache
to update your cache with latest changes
We are going to use only one migration file here, which is the default users migration shipped with laravel.
Step 2:
Migration and Seeder files:
Create a UsersTableSeeder file:
php artisan make:seeder UsersTableSeeder
Open the seeder file created and edit it as follows:
<?phpuse Illuminate\Database\Seeder;use App\User;use Faker\Factory;class UsersTableSeeder extends Seeder {public function run() { $faker = Factory::create(); User::truncate(); foreach(range(1, 100) as $i) { User::create([ 'name' => $faker->name, 'email' => $faker->unique()->email, 'email_verified_at' => now(), 'password' => bcrypt('password'), 'remember_token' => Str::random(10), ]); } }}
Save the file. Next, open the DatabaseSeeder.php
file and uncomment this line:
$this->call(UsersTableSeeder::class);
Save all files and run migration with seed:
php artisan migrate:fresh --seed
Now check your database, the users table particularly, you will see 100 random users created by the seeder.
Step 3:
Include the Javascript and CSS in the view file
We will be using the default welcome view file that laravel ships with: welcome.blade.php
<!doctype html><html lang="{{ str_replace('_', '-', app()->getLocale()) }}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="csrf-token" content="{{ csrf_token() }}"><title>Datatable</title><link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"></head><body><div id="app">{{-- This is the vue component --}}<all-users></all-users></div><script src="{{ asset('js/app.js') }}"></script></body></html>
Observe that we included the app.css , app.js the Vue component that will be used <all-users></all-users> and bootstrap cdn.
Step 4:
Create the UsersController file:
Create the UsersController file using:
php artisan make:controller UsersController
Edit the file to be identical to this:
<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;use App\User;class UsersController extends Controller {public function getUsers(Request $request) {if ( $request->input('showdata') ) {return User::orderBy('created_at', 'desc')->get();}$columns = ['name', 'email', 'created_at'];$length = $request->input('length');$column = $request->input('column');$search_input = $request->input('search');$query = User::select('name', 'email', 'created_at')->orderBy($columns[$column]);if ($search_input) {$query->where(function($query) use ($search_input) {$query->where('name', 'like', '%' . $search_input . '%')->orWhere('email', 'like', '%' . $search_input . '%')->orWhere('created_at', 'like', '%' . $search_input . '%');});}$users = $query->paginate($length);return ['data' => $users];}public function deleteUser(User $user) {if($user) {$user->delete();}return 'user deleted';}}
From the above code, we get all the users in the database, we can filter the users and can also delete them.
Step 5:
Edit the web.php file:
<?phpRoute::get('/', function () {return view('welcome');});Route::get('/users', 'UsersController@getUsers');Route::delete('/users/{user}/delete', 'UsersController@deleteUser');
Step 6:
Edit the app.js file:
require('./bootstrap');window.Vue = require('vue');Vue.component('all-users', require('./components/AllUsers.vue').default);import swal from 'sweetalert2';window.swal = swal;window.Fire = new Vue();const app = new Vue({el: '#app'});
Sweetalert2 will be used for Notification This, we will install later.
Step 7:
Create the VueJS File
Name the file AllUsers.vue because this is the name we defined in the app.js file and used in the welcome.blade.php. Note that this file should be created in the in the path: /resources/js/components
The file should be identical to:
<template><div class="users-style"><div style="margin-bottom: 20px;"><h2>Laravel and VueJS Datatable from Scratch</h2></div><div class="table-style"><input class="input" type="text" v-model="search" placeholder="Search..."@input="resetPagination()" style="width: 250px;"><div class="control"><div class="select"><select v-model="length" @change="resetPagination()"><option value="10">10</option><option value="20">20</option><option value="30">30</option></select></div></div></div><table class="table table-bordered table-responsive"><thead><tr><th v-for="column in columns" :key="column.name" @click="sortBy(column.name)":class="sortKey === column.name ? (sortOrders[column.name] > 0 ? 'sorting_asc' : 'sorting_desc') : 'sorting'"style="width: 40%; cursor:pointer;">{{column.label}}</th><th>Actions</th></tr></thead><tbody><tr v-for="user in paginatedUsers" :key="user.id"><td>{{user.name}}</td><td>{{user.email}}</td><td>{{user.created_at}}</td><td><a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false"></a><div class="dropdown-menu"><a class="dropdown-item text-primary" href="#" @click="deleteUser(user.id)">Delete User</a></div></td></tr></tbody></table><div><nav class="pagination" v-if="!tableShow.showdata"><span class="page-stats">{{pagination.from}} - {{pagination.to}} of {{pagination.total}}</span><a v-if="pagination.prevPageUrl" class="btn btn-sm btn-primary pagination-previous" @click="--pagination.currentPage"> Prev </a><a class="btn btn-sm btn-primary pagination-previous" v-else disabled> Prev </a><a v-if="pagination.nextPageUrl" class="btn btn-sm pagination-next" @click="++pagination.currentPage"> Next </a><a class="btn btn-sm btn-primary pagination-next" v-else disabled> Next </a></nav><nav class="pagination" v-else><span class="page-stats">{{pagination.from}} - {{pagination.to}} of {{filteredUsers.length}}<span v-if="`filteredUsers.length < pagination.total`"></span></span><a v-if="pagination.prevPage" class="btn btn-sm btn-primary pagination-previous" @click="--pagination.currentPage"> Prev </a><a class="btn btn-sm pagination-previous btn-primary" v-else disabled> Prev </a><a v-if="pagination.nextPage" class="btn btn-sm btn-primary pagination-next" @click="++pagination.currentPage"> Next </a><a class="btn btn-sm pagination-next btn-primary" v-else disabled> Next </a></nav></div></div></template>
<script>export default {created() {this.getUsers();Fire.$on('reloadUsers', () => {this.getUsers();})},data() {let sortOrders = {};let columns = [{label: 'Name', name: 'name' },{label: 'Email', name: 'email'},{label: 'Date Added', name: 'created_at'},];columns.forEach((column) => {sortOrders[column.name] = -1;});return {users: [],columns: columns,sortKey: 'created_at',sortOrders: sortOrders,length: 10,search: '',tableShow: {showdata: true,},pagination: {currentPage: 1,total: '',nextPage: '',prevPage: '',from: '',to: ''},}},methods: {deleteUser(id) {axios.delete(`/users/${id}/delete`).then(() => {Fire.$emit('reloadUsers')swal('Success!','User deleted','success')}).catch(() => {swal('Failed', 'There was something wrong', 'warning');});},getUsers() {axios.get('/users/', {params: this.tableShow}).then(response => {console.log('The data: ', response.data)this.users = response.data;this.pagination.total = this.users.length;}).catch(errors => {console.log(errors);});},paginate(array, length, pageNumber) {this.pagination.from = array.length ? ((pageNumber - 1) * length) + 1 : ' ';this.pagination.to = pageNumber * length > array.length ? array.length : pageNumber * length;this.pagination.prevPage = pageNumber > 1 ? pageNumber : '';this.pagination.nextPage = array.length > this.pagination.to ? pageNumber + 1 : '';return array.slice((pageNumber - 1) * length, pageNumber * length);},resetPagination() {this.pagination.currentPage = 1;this.pagination.prevPage = '';this.pagination.nextPage = '';},sortBy(key) {this.resetPagination();this.sortKey = key;this.sortOrders[key] = this.sortOrders[key] * -1;},getIndex(array, key, value) {return array.findIndex(i => i[key] == value)},},computed: {filteredUsers() {let users = this.users;if (this.search) {users = users.filter((row) => {return Object.keys(row).some((key) => {return String(row[key]).toLowerCase().indexOf(this.search.toLowerCase()) > -1;})});}let sortKey = this.sortKey;let order = this.sortOrders[sortKey] || 1;if (sortKey) {users = users.slice().sort((a, b) => {let index = this.getIndex(this.columns, 'name', sortKey);a = String(a[sortKey]).toLowerCase();b = String(b[sortKey]).toLowerCase();if (this.columns[index].type && this.columns[index].type === 'date') {return (a === b ? 0 : new Date(a).getTime() > new Date(b).getTime() ? 1 : -1) * order;} else if (this.columns[index].type && this.columns[index].type === 'number') {return (+a === +b ? 0 : +a > +b ? 1 : -1) * order;} else {return (a === b ? 0 : a > b ? 1 : -1) * order;}});}return users;},paginatedUsers() {return this.paginate(this.filteredUsers, this.length, this.pagination.currentPage);}}};</script>
Step 8:
Edit the app.scss file:
.users-style {width: 70%;margin: auto;margin-top: 20px;.table-style {margin-bottom: 10px;input {width: 175px;}.control {float: right;}}.table {width: 100%;.sorting {background-image: url('/images/sort_both.png');background-repeat: no-repeat;background-position: center right;}.sorting_asc {background-image: url('/images/sort_asc.png');background-repeat: no-repeat;background-position: center right;}.sorting_desc {background-image: url('/images/sort_desc.png');background-repeat: no-repeat;background-position: center right;}}nav a {color: white!important;margin-right: 10px;}}h1 {text-align: center;font-size: 30px;}.pagination {justify-content: flex-end !important;.page-stats {align-items: center;margin-right: 5px;}i {color: #3273dc !important;}}
Step 9:
Install npm dependencies:
npm install
Remember we used sweetalert2 aboved. install it using:
npm install sweetalert2 --save
Very important: to compile the javascript code, run:
npm run dev
Step 10:
Serve the laravel application and open in the browser using:
php artisan serve
Then visit: http://127.0.0.1:8000 and see your application running. with the randomly generated 100 users.
To further edit the javascript file, put on the watcher to compile on real time using: npm run watch
I hope you didn’t have any issues following this tutorial. If you do, please don’t hesitate to comment as i will respond promptly.
Get the code here: github
You have seen a basic Datatable from this tutorial, which can be used for literally any situation that involve data presentation in a tabular form. Be it in the SuperAdmin, or rendering whatever data of your choice.
Thank you for reading.