Control DataTables columns visibility with checkboxes - javascript

I am using datatables 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?

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>

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.

->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;
})

Related

DataTables adding data-order

I'm trying to sort my table by adding data-order in createdCell callback - it's working fine but seems table cache is not updating after that - sorting by first column (date with timestamp in data-order) simply not working.
I have tried table.rows/cells().invalidate() - no effect.
$.ajax({
type: "POST",
url: getLayoutData().urls.get_validation_history,
data: {
build_pk: build_pk,
type: validation_type,
},
success: function(response){
var response_data = JSON.parse(response);
var table = $("#validationHistoryTable").DataTable({
data: response_data.snapshots,
destroy: true,
autoWidth: false,
columns: [
{data: 'updated'},
{data: 'updated_by'},
{data: 'type'},
{data: 'status'},
{data: 'comment'},
],
columnDefs: [
{"width": "30%", "targets": 4},
{"targets": 0,
"createdCell": function(td, cellData, rowData, row, col){
raw = $(td).text().split(" ");
date = raw[0].split(".");
iso_time = date[2]+'-'+date[1]+'-'+date[0]+' '+raw[1];
$(td).attr('data-order', Date.parse(iso_time).getTime());
}
}
],
You cannot insert orthogonal data by manipulating nodes. You can manipulate existing and recognized data-* values through nodes and invalidate(), but not as part of the post processing of DOM nodes. Look at https://datatables.net/manual/data/orthogonal-data. data-* values can be specified by
Markup
A render literal that points to an alternative JSON property
A render callback
See proof of concept in this little example -> http://jsfiddle.net/rtu0bjz6/
{
targets: 2,
createdCell: function(td, cellData, rowData, row, col){
counter++
$(td).attr('data-order', counter)
}
}
Does not have any effect. The column is sorted by its original data, not its data-order. However, if you are using a render() function and return a special value upon type "sort" then it works as expected.
{
targets: 3,
render: function ( data, type, row, meta ) {
return type == 'sort' ? meta.row : data
}
}
So in your case, you could do something like (not tested) :
{
targets: 0,
render: function ( data, type, row, meta ) {
if (type == 'sort') {
var raw = data.split(" ");
var date = raw[0].split(".");
var iso_time = date[2]+'-'+date[1]+'-'+date[0]+' '+raw[1];
return Date.parse(iso_time).getTime()
} else {
return data
}
}
}

Delete a record from Ag-grid using rendered button in Vue file

I'm using an ag-grid to populate list records(rows) in a table. I'm trying to delete a record from the grid-table when the user clicks a "Delete" button in one of the cell. I'm able to add the "Delete" button to the grid but how can I capture the index of the clicked "Delete" in the table and remove that row.
table's grid options are defined in file specials.Vue
*function setGridOptions() {
this.gridOptions = {
columnDefs: [{
headerName: 'Model',
field: 'scModel',
editable: params => this.isCellEditable(params),
newValueHandler: this.NewValueHandler,
width: 120,
},
{
headerName: 'Delete ',
width:50,
field: 'delete',
cellRenderer: deleteRecordCellRender,
},
],
rowData: this.getInitialRowData(),
headerHeight: 36,
rowHeight: 28,
suppressMovableColumns: true,
suppressMenuMainPanel: true,
suppressMenuFilterPanel: true,
suppressMenuColumnPanel: true,
suppressContextMenu: true,
singleClickEdit: true,
rowSelection: 'single',
};
}*
The 'Delete' column rendered from 'deleteRecordCellRender' defined in javascript file ag-gridFunction.js
*export function deleteSpecialRecordCellRender() {}
deleteSpecialRecordCellRender.prototype.init = deleteSpecialRecordCellRenderInit;
deleteSpecialRecordCellRender.prototype.getGui = deleteSpecialRecordCellRenderGetGui;
function deleteSpecialRecordCellRenderGetGui() {
return this.eGui;
}
function deleteSpecialRecordCellRenderInit() {
console.log('deleteSpecialRecordCellRenderInit this: ', this);
this.eGui = `<button class="btn btn-default btn-sm delete-row-button" rel="">Delete</a>`;
}*
delete-row-button function defined in specials.Vue file is
$('.delete-row-button').on('click', () => {
console.log('made it');
console.log('scope: ', this);
console.log('clicked row',$scope.gridOptions.api.getSelectedRows())
});
But It's not getting the selectedRow due to a scope issue. How do I get the selected row index and delete after ?

Store calculated data in Column of Kendo Grid

What I'm trying to do is store some data in a specific column that is calculated by using the data from another column.
I currently have a function that returns the number of available licenses for the given Id in JSON
function getAvailableLicenses(id) {
var url = "/Host/Organization/AvailableLicenses/" + id;
$.get(url, function (data) {
return data.AvailableLicenses;
});
}
How do I go about storing this number in a column named "AvailableLicenses"?
Here is my current Grid:
$("#OrganizationGrid").kendoGrid({
dataSource: viewModel.get("orgDataSource"),
filterable: {
extra: false
},
sortable: true,
pageable: true,
columns: [
{ field: "Id", hidden: true },
{ field: "Name", template: "<a href='/Host/Organization/Detail/#:Id#'>#:Name#</a>" },
{ field: "LicenseNumber", title: "Number of Licenses" },
{ field: null, title: "Available Licenses", template: "#= getAvailableLicenses(Id) #" },
{ field: "LicenseExpiration", title: "License Expiration", format: "{0:MM/dd/yyyy}" },
{ field: "State" },
{ field: "Active" }
],
editable: false
});
As you can see, I tried to create a null column with a template that calls the function for the given Id.
By using Fiddler I can see that the function is indeed being called for all of the rows, but the AvailableLicenses column just displays Undefined for every row.
Is there something I'm missing here to get this to work?
I think the better way to do this is on dataSource parse() function
First: you column configuration must change like this:
{ field: "AvalableLicenses", title: "Available Licenses" },
You alaways can use you template .
And second, inside your dataSource() you can add:
schema: {
parse: function(response) {
for (var i = 0; i < response.length; i++) {
response[i].AvalableLicenses= null;
response[i].AvalableLicenses = getAvailableLicenses(response[i].Id)
}
return response;
}
}
EDIT:
If you prefer using you way, I dont see any problem in your configuration, probably your $.get is returning undefined, or something you don't expect.
For conviniance I did an example working.
http://jsfiddle.net/jwocf897/
Hope this help

How to calculate row sum on user entry in free jqgrid

Row sum should calculated if price or quantity is changed.
Formatter is created according to How to access other row data from a cell's custom formatter in JqGrid
In inline and row editing modes row sum doesnt change.
Row sum column is read only and does not have id assigned. So it is difficult to create script to change it.
How to fix this so that row sum is calculated on edit ?
Testcase is in http://jsfiddle.net/ex6158L1/4/ containing
javascript
var serverResponse = {
"page": "1",
"records": "3",
"rows": [
{ "Id": "1", Quantity:4, Price : 23.33 },
{ "Id": "2", Quantity:4.55, Price : 76.66 }
]
},
$grid = $("#categorysummary");
function sumFmatter (cellvalue, options, rowObject) {
return options.rowData.Quantity *
options.rowData.Price;
}
$grid.jqGrid({
url: "/echo/json/", // use JSFiddle echo service
datatype: "json",
mtype: "POST", // needed for JSFiddle echo service
pager: '#pager',
postData: {
json: JSON.stringify(serverResponse) // needed for JSFiddle echo service
},
//colNames: ["Quantity", "Price"],
colModel: [
{ name: 'Id', hidden: true },
{ name: 'Quantity', editable: true},
{ name: 'Price', editable: true },
{ name: "Sum", formatter: sumFmatter, editable: "readonly" }
],
jsonReader: {
id: 'Id',
repeatitems: false
},
sortname: 'Id',
viewrecords: true
})
.jqGrid("navGrid",'#pager')
.jqGrid("inlineNav",'#pager');
and css
<div class="container">
<div id="pager"></div>
<table id="categorysummary"></table>
</div>
The first problem, which you have, is the bug in free jqGrid, which follows that options.rowData was not filled inside of setRowData. I posted the corresponding fix to GitHub to eliminate the problem.
The next problem is the requirement to reformat the data of editable: "readonly". Form editing do this, but not inline editing. One can use either editable: "hidden" or to add the option
inlineEditing: {
extraparam: { Sum: "" }
}
which extend the results of inline editing with the dummy value "" for Sum column. It force reformatting of the value in the Sum column.
I would recommend you additionally to change the formatter sumFmatter which you use to
function sumFmatter (cellvalue, options, rowObject) {
var quantity = parseFloat(options.rowData.Quantity || 0),
price = parseFloat(options.rowData.Price || 0);
return (quantity * price).toFixed(2);
}
see the demo http://jsfiddle.net/OlegKi/ex6158L1/7/ or to
function sumFmatter (cellvalue, options, rowObject, action) {
var quantity = parseFloat(options.rowData.Quantity || 0),
price = parseFloat(options.rowData.Price || 0);
return $.fn.fmatter.call(this, "number", quantity * price,
options, rowObject, action);
}
where I called formatter: "number" to format the results of the multiplication (quantity * price).
See the resulting demo http://jsfiddle.net/OlegKi/ex6158L1/10/

Dgrid - Display label for number (i.e. 02 = Cat) I want to display Cat - not the number

In my Dgrid I have a column that displays the code (in number format) for an event.
enter image description here
I want to display the label not the number in the dgrid. So if 1 = Cat. In the database it shows as a 1 - but I want to display 'Cat' in dgrid. Can't find anything on how to do this.
Help or a lead in a direction would be helpful. Thanks!!
UPDATED: 6.16.15
Here is the code. I'm limited in what I can show.
These are some of the codes. 02 = XXXXX, 03 = XXXXX1, and so on and so on. Right now, the dgrid displays the numbers. It's kind of like a key. I need it to display what the number represents in the dgrid, not the number. So 02 should display 'Traffic Stop'. Not sure how to do a jsfiddle yet, and don't have a whole lot of extra time at the moment. I'm limited in what info I can give out, so I'd have to recreate a dummy version.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>XXXXXXXX Events</title>
<link rel="stylesheet" href="/static/web_ui/dgrid/css/skins/slate.css">
<h1>XXXXXXXX Events</h1>
<form id="queryForm">
<label for="XXXXXField">XXXXX Type contains:</label>
<input id="XXXXXField" name="event_type">
<button type="submit">Filter</button>
<button type="reset">Reset</button>
</form>
<script src="/static/web_ui/dojo/dojo.js"
data-dojo-config="async: true"></script>
<script>
require([
'dojo/_base/declare',
'dojo/dom',
'dojo/on',
'dstore/Rest',
'dstore/Request',
'dgrid/extensions/ColumnResizer',
'dgrid/extensions/ColumnReorder',
'dgrid/CellSelection',
'dgrid/extensions/DijitRegistry',
// 'dstore/Memory',
// 'dstore/Trackable',
// 'dstore/Cache',
'dgrid/OnDemandGrid'
// 'dojo/domReady!'
], function (declare, dom, on, Rest, Request, ColumnResizer, ColumnReorder, CellSelection, DijitRegistry, OnDemandGrid) {
var store = new Rest({target:'/api/XXXXXXEvents/?format=json',
sortParam: 'ordering', ascendingPrefix:'', descendingPrefix:'-'
});
// var cacheStore = Cache.create(store, {
// cachedStore: new (Memory.createSubclass(Trackable)) ()
// });
var grid = window.grid = new (declare([OnDemandGrid, ColumnResizer, ColumnReorder, CellSelection, DijitRegistry])) ({
collection: store,
selectionMode: 'single',
sort: 'id',
// idProperty: 'id',
columns: [
{field: 'id', label:'ID', resizeable: false},
{field: 'XXXXX_type', label:'XXXXX Type', resizeable: false},
{field: 'XXXXX_at', label:'XXXXX Time', resizeable: false},
{field:'XXXXX', label:'XXXXX Count', resizeable: false},
{field:'XXXXX', label:'XXXXX', resizeable: false},
{field:'XXXXX_info', label:'XXXXX Info', resizeable: false},
{field:'hidden', label:'Hidden', resizeable: false},
{field:'XXXXX', label:'XXXXX', resizeable: false},
{field:'XXXXX', label:'XXXXX', resizeable: false}
]
}, 'grid');
grid.startup();
on(dom.byId('queryForm'), 'submit', function(event) {
event.preventDefault();
grid.set('collection', store.filter({
// Pass a RegExp to Memory's filter method
// Note: this code does not go out of its way to escape
// characters that have special meaning in RegExps
last: new RegExp("^\d+$")
}));
});
on(dom.byId('queryForm'), 'reset', function() {
// Reset the query when the form is reset
grid.set('collection', store);
});
});
</script>
</head>
<body class="slate">
<div id="grid"></div>
</body>
</html>
You need to use the column formatter function for rendering data.
check the jsfiddle over here.
Check the examples over here
I have taken this example and modified as per your needs.
require([
'dgrid/Grid',
'dojo/domReady!'
], function(Grid) {
var data = [
{ id: 1, number: 7 },
{ id: 2, number: 8 },
{ id: 3, number: 9 }
];
function testFormatter(item){
//console.log(item,typeof(item));
var newItem;
if ( item == 7 )
newItem = 'Dog'
else if ( item == 8 )
newItem = 'Cat'
else if ( item == 9 )
newItem = 'Bird'
return newItem;
}
var columnsFormatter = [
{
label: "Number",
field: "number",
formatter: testFormatter
}
];
var grid = new Grid({
columns: columnsFormatter
}, "gridcontainer");;
grid.renderArray(data);
});

Categories

Resources