Damon
Damon

Reputation: 4484

Control DataTables columns visibility with checkboxes

I am using to dynamically render a table on my blade template. I have a series of checkboxes that user can check to show/hide table columns. All of this is working great.

This is what my template looks like:

template.blade.php

<table id="dataTables-report" class="table table-striped table-bordered table-hover">
</table>

Here is what I am using to render the table:

scripts.js

$('#dataTables-report').DataTable({
  ...
  columnDefs: [
      {
          targets: 0,
          title: 'Name',
          searchable: true,
          data: function (row, type, val, meta) {
              // return row.data;
          }
      },
      @if($report->order_date)
            {
                targets: 1,
                title: 'Order Date',
                searchable: false,
                data: function (row, type, val, meta) {
                    // return row.data;
                }
            },
       @endif
       @if($report->order_number)
            {
                targets: 2, // could be 1 if order date is not selected
                title: 'Order Number',
                searchable: false,
                data: function (row, type, val, meta) {
                    // return row.data;
                }
            },
       @endif
      ...
});

"Order Date" is a checkbox that a user can choose to display on the table. If it is checked, it shows that column. Otherwise it does not. It is possible that a different column could be selected first and it could be targets: 1. Now if a user checks another box, targets needs to dynamically get set to the next number. In this case: targets: 2.

Each checkbox is stored as it's own column in the database, so I don't think I can do any sort of loop (hence a bunch of if statements). Otherwise, I think something like this would work.

Is there a way to dynamically generate the targets number right in my blade template?

Upvotes: 1

Views: 1848

Answers (3)

Socheat or
Socheat or

Reputation: 1

->addColumn('action', function ($floor) {
    $action=  
    @Can("floor-edit"){"
         <a class='btn btn-info  btn-sm' 
             href=".route("floor.edit",Crypt::encrypt($floor->id))."><i class='fa fa-edit'></i>
         </a>
         <button type='button' name='delete' id=".Crypt::encrypt($floor->id)." class='delete btn btn-danger btn-sm'><i class='fa fa-trash'></i></button>
   "};
   return $action;
})

Upvotes: 0

Yevhen Horbunkov
Yevhen Horbunkov

Reputation: 15530

If you're seeking truly dynamic column visibility controlled by checkboxes (as I understood your ultimate goal), it can be done user-end entirely by few lines of jQuery.

In order to do that, you may simply

  • append source object property of each column as a value attribute to your <input> nodes:
  • upon change event, find the column that is sourced (using column().dataSrc() method) by the object property that corresponds to clicked checkbox value and adjust that column visibility (using .column().visible() method accordingly:
$('#checkboxWrapper').on('change', '[type="checkbox"]', event => {
  let colindex = null;
  dataTable.columns().every(function(){
    if(this.dataSrc() == $(event.target).val()) colindex = this.index();
  });
  dataTable.column(colindex).visible($(event.target).prop('checked')).draw();
});

Complete live demo of that concept you may find below:

//sample source data
const dataSrc = [
  {id: 1, item: 'apple', cat: 'fruit'},
  {id: 2, item: 'carrot', cat: 'vegie'},
  {id: 3, item: 'banana', cat: 'fruit'}
];

//extract all unique object keys from data source array
const checkboxes = [...new Set(dataSrc
  .map(item => Object.keys(item))
  .flat())];
//translate those into <input> nodes HTML
const checkboxesHtml = checkboxes.reduce((inputs, prop) => inputs += `<input type="checkbox" value="${prop}" checked>${prop}</input>`,'');
$('#checkboxWrapper').append(checkboxesHtml);

//initialize datatables
const dataTable = $('#example').DataTable({
  data: dataSrc,
  dom: 't',
  columns: checkboxes.map(prop => ({title: prop, data: prop}))
});

//control columns visibility with checkboxes
$('#checkboxWrapper').on('change', '[type="checkbox"]', event => {
  //grab column().index() that corresponds to checkbox value
  let colindex = null;
  dataTable.columns().every(function(){
    if(this.dataSrc() == $(event.target).val()) colindex = this.index();
  });
  //toggle selected column visibility
  dataTable.column(colindex).visible($(event.target).prop('checked')).draw();
});
<!doctype html>
<html>
<head>
  <script type="application/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  <script type="application/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
</head>
<body>
  <div id="checkboxWrapper"></div>
  <table id="example"></table>
</body>
</html>

Upvotes: 1

Damon
Damon

Reputation: 4484

Thank you for your suggestions, here is what I came up with as a "quick" solution while I look further into your recommendations.

In my blade template, I created a global variable that I could access within my php.

@section('scripts')
    <script>
        $(function () {
            ...
            let columnTarget = 0;

            ...

            $('#dataTables-report').DataTable({
                ...
               columnDefs: [
               {
                   targets: columnTarget,
                   title: 'Name',
                   searchable: true,
                   data: function (row, type, val, meta) {
                       // return row.data;
               }
           },
           @if($report->order_date)
           {
               targets: ++columnTarget,
               title: 'Order Date',
               searchable: false,
               data: function (row, type, val, meta) {
                  // return row.data;
               }
           },
           @endif
           @if($report->order_number)
           {
               targets: ++columnTarget,
               title: 'Order Number',
               searchable: false,
               data: function (row, type, val, meta) {
                // return row.data;
               }
           },
           @endif
         ...
    </script>
@endsection

This seems to work well; correctly (dynamically) assigning the targets value.

Upvotes: 0

Related Questions