While generating the tables in QuickAdminPanel, our last column is the one with links to View/Edit/Delete record. One of our customers asked: what if you want to have that column as first one, in the very beginning? Tricky part is how to do that with AJAX Datatable module. Let’s take a look.


Step 1. Starting Point: Default AJAX Datatable Code

Here’s the default table:

AJAX Datatables example

To load it, we have code in a few different parts.

First, Blade file – resources/views/projects/index.blade.php:

Table itself – without data, cause it’s loaded with AJAX call later.

<table class=" table table-bordered table-striped table-hover ajaxTable datatable datatable-Project">
    <thead>
        <tr>
            <th width="10">

            </th>
            <th>
                {{ trans('cruds.project.fields.id') }}
            </th>
            <th>
                {{ trans('cruds.project.fields.name') }}
            </th>
            <th>
                {{ trans('cruds.project.fields.description') }}
            </th>
            <th>
                 
            </th>
        </tr>
    </thead>
</table>

And then JavaScript part to load Datatables:

@section('scripts')
    @parent

    <script>
      $(function () {
        let dtButtons = $.extend(true, [], $.fn.dataTable.defaults.buttons)
        let dtOverrideGlobals = {
            buttons: dtButtons,
            processing: true,
            serverSide: true,
            retrieve: true,
            aaSorting: [],
            ajax: "{{ route('admin.projects.index') }}",
            columns: [
              {data: 'placeholder', name: 'placeholder'},
              {data: 'id', name: 'id'},
              {data: 'name', name: 'name'},
              {data: 'description', name: 'description'},
              {data: 'actions', name: '{{ trans('global.actions') }}'}
            ],
            order: [[1, 'desc']],
            pageLength: 100
          }
        $('.datatable-Project').DataTable(dtOverrideGlobals)

        $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
          $($.fn.dataTable.tables(true)).DataTable()
            .columns.adjust()
        })
      })

    </script>
@endsection

Data comes from the Controller index() method, with help of jQuery Datatables Laravel package.

Here’s app/Http/Controllers/Admin/ProjectsController.php:

$query = Project::query()->select(sprintf('%s.*', (new Project)->table));
$table = Datatables::of($query);

$table->addColumn('placeholder', ' ');
$table->addColumn('actions', ' ');

$table->editColumn('actions', function ($row) {
    $viewGate      = 'project_show';
    $editGate      = 'project_edit';
    $deleteGate    = 'project_delete';
    $crudRoutePart = 'projects';

    return view('partials.datatablesActions', compact(
        'viewGate',
        'editGate',
        'deleteGate',
        'crudRoutePart',
        'row'
    ));
});

$table->editColumn('id', function ($row) {
    return $row->id ? $row->id : "";
});
$table->editColumn('name', function ($row) {
    return $row->name ? $row->name : "";
});
$table->editColumn('description', function ($row) {
    return $row->description ? $row->description : "";
});

$table->rawColumns(['actions', 'placeholder']);

return $table->make(true);

It may look a bit complicated, but what is happening here is just getting the query from the database and overriding some logic for some columns, including actions column that we care about.

So, how to move it to the front?


Step 2. Changing Blade Columns Order

To change the order of the columns, it’s generally enough to move <th> elements as you wish, and then keep the same order in JavaScript columns array.

So, in changed resources/views/projects/index.blade.php we have this.

1. We move the empty th element from the last position to the second one. First column is reserved for checkboxes for mass-delete operation.

<table class=" table table-bordered table-striped table-hover ajaxTable datatable datatable-Project">
    <thead>
        <tr>
            <th width="10">

            </th>
            <th>
                 
            </th>
            <th>
                {{ trans('cruds.project.fields.id') }}
            </th>
            <th>
                {{ trans('cruds.project.fields.name') }}
            </th>
            <th>
                {{ trans('cruds.project.fields.description') }}
            </th>
        </tr>
    </thead>
</table>

2. We change the order in columns array of the same file:

ajax: "{{ route('admin.projects.index') }}",
columns: [
  {data: 'placeholder', name: 'placeholder'},
  {data: 'actions', name: '{{ trans('global.actions') }}'},
  {data: 'id', name: 'id'},
  {data: 'name', name: 'name'},
  {data: 'description', name: 'description'}
],

3. In the same file below, we need to change the default order, because by default it orders by Column 1 Desc:

order: [[1, 'desc']],

And now Column 1 is the “actions” one – columns are enumerated starting from 0.

So we need to change the order to this:

order: [[2, 'desc']],

And here’s the result:

AJAX Datatables Columns

As we wanted, the buttons are on the left side, right?

Not done yet, there’s one more thing we need to take care of.


Step 3. Make This New Column Not Searchable

Datatable will work now, but if we try to search on top-right, it will fail:

AJAX Datatables Search Error

The reason is that it’s trying to search in “actions” column, and can’t find it in database. And that’s true, it’s our own custom column which is not corresponding to any database column.

There’s another file you should know, which contains main Datatables settings.
It’s resources/views/layouts/admin.blade.php, and it contains this JavaScript:

  $.extend(true, $.fn.dataTable.defaults, {
    language: {
      url: languages['{{ app()->getLocale() }}']
    },
    columnDefs: [{
        orderable: false,
        className: 'select-checkbox',
        targets: 0
    }, {
        orderable: false,
        searchable: false,
        targets: -1
    }],
    select: {
      style:    'multi+shift',
      selector: 'td:first-child'
    },
    order: [],
    scrollX: true,
    pageLength: 100,
    dom: 'lBfrtip<"actions">',
    buttons: [
      {
        extend: 'copy',
        className: 'btn-default',
        text: copyButtonTrans,
        exportOptions: {
          columns: ':visible'
        }
      },
      // ... other buttons
    ]
  });

What we care about here is columnDefs parameter – it contains array of column settings that are applicable to all datatables.

In our case, by default column number 0 (targets: 0) is for checkboxes for multi-delete.

And then there’s targets: -1 which means the LAST column, which is reserved for View/Edit/Delete buttons, so it should be not orderable and not searchable.

So what we need to change here is to override that default setting in our own Projects CRUD’s JavaScript.

Here’s our updated resources/views/projects/index.blade.php:

columns: [
  {data: 'placeholder', name: 'placeholder'},
  {data: 'actions', name: '{{ trans('global.actions') }}', orderable: false, searchable: false },
  {data: 'id', name: 'id'},
  {data: 'name', name: 'name'},
  {data: 'description', name: 'description', orderable: true, searchable: true }
],

And that’s it, we have the final result – search is working again!

AJAX Datatables Search


For more information on AJAX Datatables module, read this article or watch this video.

Also, check out documentation of underlying Laravel package: Yajra Datatables.