Laravel datatable with Grid.js

Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

March 23, 2021 · 4 min read

Laravel datatable with Grid.js

Update Notice:

We’ve fixed the code for making the Single Action Controller and added some clarification. At the beginning of the article, we assume you’ve got a Laravel project ready to go. To keep this guide short, we left out the initial steps of making a new Laravel project and filling the database with data. The final design in the screenshot is made using Tabler, a free dashboard template that works with Bootstrap 5. This article doesn’t talk about how to style things, because we’re focusing on how it works. But Grid.js can be modified a lot, and you can learn how in their docs.

Dealing with big data in web development

When you’re making websites, showing users a lot of data can be tough. Because Bootstrap isn’t using jQuery anymore, developers can’t use the DataTables plugin for showing a lot of data. This change has made developers look for other ways to do this. At Lucky Media, we make custom designs with TailwindCSS. This made us look for JavaScript options that meet our high standards.

Using Grid.js for big data

We found Grid.js, a powerful, free, open-source JavaScript plugin for making tables. Grid.js is great because it works with many JavaScript frameworks like React, Angular, Vue, and even VanillaJs. In this guide, we’ll show you how to use Grid.js with server-side rendering in a Laravel 8 project.

Setting Up Grid.js

npm install gridjs

To begin, we’ll install Grid.js in our project. We have the Client model with more than 100 records, and we’ll use Grid.js to display all this data on the frontend. You can also use a seeder for this purpose.

Creating a Single Action Controller

First, we’ll create a single action controller that fetches all the clients and returns a JSON response that we can use on the frontend. To create the invokable controller, use the following command:

php artisan make:controller Actions\\FetchClientsController --invokable

This command stores our new invokable controller in an Actions folder, helping to keep our controllers clean and organized.

Here’s what our FetchClientController looks like:

namespace App\Http\Controllers\Actions;

use App\Http\Controllers\Controller;
use \Illuminate\Http\JsonResponse;
use App\Models\Client;

class FetchClientsController extends Controller
{
    public function __invoke(): JsonResponse
    {
        $clients = Client::all()
            ->transform(function($client){
                return [
                    'id' => $client->id,
                    'full_name' => "$client->name $client->surname",
                    'number' => $client->number,
                    'street' => $client->street,
                    'edit_url' => route('clients.edit', $client->id)
                ];
            });

        return response()->json($clients);
    }
}

Here on the controller we get all our Clients, and then using the transform function we only return the needed fields in the frontend. Note the last line for edit_url, we will use it to automatically get the URL for editing data so we can use it in the table.

Configuring routes

We navigate to the routes folder and we access web.php file to reference our new controller like so:

use Illuminate\Support\Facades\Route;
use \App\Http\Controllers\ClientController;
use \App\Http\Controllers\Actions\FetchClientsController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Auth::routes();

Route::group(['middleware' => 'auth'], function () {

    // Single Action Controllers
    Route::get('/clients/fetch', FetchClientsController::class)->name('clients.fetch');
    
    // Resource Controllers
    Route::resource('clients', ClientController::class);
});

Rendering the table from JavaScript

Now, all we have to do is configure our index file in order to render our table from JavaScript.

In our resource/views/clients/index.blade.php we have the following code:

@extends('layouts.app', ['title' => 'Clients'])

@section('content')
    <div class="row">
        <div class="col-lg-12">
            <div js-hook-url="{{ route('clients.fetch') }}" js-hook-table-client></div>
        </div>
    </div>
@endsection

In this example we use two HTML attributes that we will use in our JavaScript file, the first one is for retrieving our URL where we fetch all the clients, and we are going to use the second one as a reference to render our table from JavaScript.

In our app.js file under resources/js/app.js we need to add the following code.

import { Grid, html } from "gridjs";
import "gridjs/dist/theme/mermaid.css";

const TABLE_CLIENTS = '[js-hook-table-client]'

// Get the table element
const table_clients_wrapper = document.querySelector(TABLE_CLIENTS);

// Get the url attribute
const table_clients_url = table_clients_wrapper.getAttribute('js-hook-url');

if (table_clients_wrapper) {
    const table_clients = new Grid({
        columns: [
            {
                name: 'Full Name'
            },
            {
                name: 'Number'
            },
            {
                name: 'Street'
            },
            {
                name: 'Actions',
                // Here we inject our route edit
                formatter: (_, row) => html(`<a href='${row.cells[3].data}'>Edit</a>`)
            }
        ],
        search: {
            enabled: true
        },
        server: {
            // Here we give the URL we passed in the hook
            url: table_clients_url,
            then: data => data.map(table => [table.full_name, table.number, table.street, table.edit_url]),
            handle: (res) => {
                // no matching records found
                if (res.status === 404) return {data: []};
                if (res.ok) return res.json();

                throw Error('oh no :(');
            },
        },
        pagination: {
            enabled: true,
            limit: 10,
            summary: false
        },
    }).render(table_clients_wrapper);
}

So, what are we doing here? We instantiate a new Grid class and pass the options as objects. The columns array represents all the table columns, and the last item is used to inject our edit route. In the server object, we provide the URL parameter that we called earlier, and after the data has been loaded, we map each piece of data coming from the server to their respective columns.

The pagination part is self-explanatory. We have enabled pagination and set the limit of rows to be displayed at 10.

You can read more about this in the official Grid.JS documentation which covers a lot of use cases.

After this, you have to run npm run dev in order to compile all the assets. If you did everything correctly then you should see the following screen:

Table Image

Improve your loading times

Tip: You can use the following library to cache your models and supercharge your loading times. We highly recommend it for large data: laravel-model-caching.


Bring Your Ideas to Life 🚀

If you need help with a Laravel project let’s get in touch.

Lucky Media is proud to be recognized as a leading Laravel Development Agency

Technologies

Laravel
Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

Founder and CEO of Lucky Media

Stay up-to-date

Be updated with all news, products and tips we share!

Let’s chat

We partner with a limited number of brands each quarter to ensure senior-level attention on every project.

lokman and arlind headshots
Teamwork