Is there a way to hide a DataTables column based on its type? - datatables

I have this code currently:
drawCallback: function () {
let api = this.api();
// hide columns that add up to 0
api.columns().every(function (i) {
let sum = this.data().sum();
if (sum === 0 && typeof sum === 'number' && i !== 0) {
api.column(i).visible(0);
}
});
}
There is an issue with it, however... for one particular column where the values are just a single word, such as "Completed" or "Pending", it seems as though this.data().sum(); will result in 0 rather than NaN.
The first two columns will result in NaN, seemingly because they are multi-word sentences, which is the only difference between these two columns.
This is a sample of the data
<td>Foo Bar Jones Jonesy McFoo Department</td>
<td>ABC CityName Location Code XXFOO</td>
<td data-sort="20201002">10/02/2020</td>
<td>Pending</td>
<td>(a variable number of columns with data that is some number that is possibly zero or greater)</td>
Isn't there some way to check the column type with api.columns().every()?

I am not able to recreate your specific error with single-word cell values vs. multi-word cell values. When I use parseFloat('x') vs. parseFloat('x y z'), they both evaluate to NaN.
(Also, I don't know how you implemented your sum() function, so that may explain what you are seeing.)
I am going to use the following example from here, as a starting point for my sum() - but with a modification:
$.fn.dataTable.Api.register( 'column().data().sum()', function () {
return this.reduce( function (a, b) {
var x = parseFloat( a ); // removed the 'or' operator: || 0;
var y = parseFloat( b ); // removed the 'or' operator: || 0;
return x + y;
} );
} );
My change (removing the "or" operators) ensures NaN values are not set to zero, but remain as NaN - which is what you need for your specialized sum() function.
Now, if I use this with your DataTable code, and some test data based on what you provided, I get the expected outcome:
As a side note, dates may be parsed into numbers OK, depending on the data format - which can explain why date columns can show up OK (they sum to a positive number).
Here is the full standalone demo, in case it helps.
In this example, all the data in the final column ("Val 3") are zeros - so that column is hidden. The column remains hidden if there are non-zero values which sum up to zero, also:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.21/css/jquery.dataTables.min.css">
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
</head>
<body>
<div style="margin: 20px;">
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Dept</th>
<th>Loc</th>
<th>Date</th>
<th>Status</th>
<th>Val 1</th>
<th>Val 2</th>
<th>Val 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Foo Bar Jones Jonesy McFoo Department 1</td>
<td>ABC CityName Location Code XXFOO</td>
<td data-sort="20200119">19/01/2020</td>
<td>Active</td>
<td>1</td>
<td>1</td>
<td>0</td>
</tr>
<tr>
<td>Foo Bar Jones Jonesy McFoo Department 2</td>
<td>DEF CityName Location Code XXFOO</td>
<td data-sort="20201002">10/02/2020</td>
<td>Pending</td>
<td>0</td>
<td>2</td>
<td>0</td>
</tr>
<tr>
<td>Foo Bar Jones Jonesy McFoo Department 3</td>
<td>XYZ CityName Location Code XXFOO</td>
<td data-sort="20201016">16/02/2020</td>
<td>Pending</td>
<td>0</td>
<td>1</td>
<td>0</td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript">
$.fn.dataTable.Api.register( 'column().data().sum()', function () {
return this.reduce( function (a, b) {
var x = parseFloat( a );
var y = parseFloat( b );
return x + y;
} );
} );
$(document).ready(function() {
var table = $('#example').DataTable( {
drawCallback: function () {
let api = this.api();
// hide columns that add up to 0
api.columns().every(function (i) {
let sum = this.data().sum();
if (sum === 0 && typeof sum === 'number' && i !== 0) {
api.column(i).visible(0);
}
});
}
} );
} );
</script>
</body>
</html>

Related

Jquery Datatables plugin. How to read the HTML Column Heading value

I am using the JQuery Datatables plugin (https://datatables.net/).
I would like to be able to reference the original HTML <th></th> column header values.
<table border="0">
<thead>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
</thead>
<tbody>
<tr>
<td>data for column 1</td>
<td>data for column 2</td>
<td>data for column 3</td>
</tr>
</tbody>
</table>
I know that columns can be named within the plugin using the columns property:
$('#mytable').DataTable(
{
columns: [
{name: 'column1'},
{name: 'column2'},
{name: 'column3'}
]
}
)
However it would be very useful when dealing with dynamically created HTML to be able to reference the HTML <th> tag in order to find the Datatables index for the column with a specific name.
I have had a look at dataTable.context[0].aoHeader and can see both and idx and innerHTML objets within that, however it looks like table().header() might provide a solution.
You can use the column.name values shown in the question to select the DataTables column index values.
Example:
table.column( 'column2:name' ).index()
Demo:
$(document).ready(function() {
var table = $('#mytable').DataTable(
{
columns: [
{ name: 'column1' },
{ name: 'column2' },
{ name: 'column3' }
]
}
)
let idx = table.column( 'column2:name' ).index();
console.log( idx ); // prints 1 (2nd column's index)
} );
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="https://code.jquery.com/jquery-3.5.0.js"></script>
<script src="https://cdn.datatables.net/1.12.1/js/jquery.dataTables.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.12.1/css/jquery.dataTables.css">
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
</head>
<body>
<div style="margin: 20px;">
<table id="mytable" class="display dataTable cell-border" style="width:100%">
<thead>
<th>Column1</th>
<th>Column2</th>
<th>Column3</th>
</thead>
<tbody>
<tr>
<td>data for column 1</td>
<td>data for column 2</td>
<td>data for column 3</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
This uses the column() API function, together with a column selector.
There are various different ways you can provide a column selector - one of which is the way I show in the above code:
'YOUR_COL_NAME_HERE:name'
Then the index() function returns the zero-based column index.
If you have draggable columns (i.e. columns can be reordered) then you may also need to add a selector modifier to account for that, depending on what specific index you want - the original one, or the currently displayed one.

DataTables export to csv with format Syntax error

I have datatables and I try to export it to CSV. The column 7 is a date.
text: 'Csv',
extend: 'csvHtml5',
filename: "customers",
exportOptions: {
columns: [1, 2, 7,8],
format: {
body: function (data, row, column, node) {
//check if type is input using jquery
console.log(data)
return $(data).is("input") ? $(data).val() : data;
}
}
}
It works all OK if I don't use "format" but with format I have error:
Uncaught Error: Syntax error, unrecognized expression: 13/11/2021
When I log "data" without format, it is all OK. It looks like the slashes in date make problems in "format".
Any help:)
edit:
the date I take from firestore
let options = {
year: 'numeric',
month: '2-digit',
day: 'numeric',
//hour: 'numeric',
// minute: 'numeric'
// second: 'numeric'
};
dataSet.push([
...
oc.data().created ? doc.data().created.toDate().toLocaleDateString("en-GB", options) : "",
...
This is caused by your jQuery $(data) selector when the data variable contains one or more forward slashes. See:
Selector with string space and /; $(" /") throws uncaught error in jquery
Why is the slash character causing my selector to fail?
And probably others.
A simple (but potentially error-prone) approach would be to write some logic like this:
// possible, but not recommended:
data.startsWith('<input') ? $(data).val() : data
Instead of this type of approach, you can use DataTables' support for orthogonal data.
In this new approach we will target the column which contains <input> fields, and specify two different versions of these data values for that column:
The version we want to display in the table (i.e. the actual input HTML).
The version we want to export (i.e. the value contained in the input field).
To do this we have to specify this for the relevant column (or columns). In my test example, this is the second column - so col index 1:
columnDefs: [
{
targets: [1],
render: function (data, type, row) {
return type === 'export' ? $(data).val() : data;
}
}
]
Here, I have specified that there will be a custom data type called export for the 2nd column - and its value will be the result of $(data).val() - and all other built-in orthogonal data types (display, sort, filter) will just use the raw data value.
Now I can use this new orthogonal data type in my export section of the DataTable:
exportOptions: {
columns: [0, 1, 4, 5],
orthogonal: 'export'
}
The overall solution using my test data is as follows:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.23/js/jquery.dataTables.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.23/css/jquery.dataTables.min.css"/>
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/1.6.5/css/buttons.dataTables.min.css"/>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/dataTables.buttons.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/buttons.colVis.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/buttons.html5.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.6.5/js/buttons.print.min.js"></script>
</head>
<body>
<div style="margin: 20px;">
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Age</th>
<th>Start date</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td><input type="text" value="System Architect"></td>
<td>Edinburgh</td>
<td>61</td>
<td>13/11/2021</td>
<td>$320,800</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td><input type="text" value="Accountant"></td>
<td>Tokyo</td>
<td>63</td>
<td>14/12/2021</td>
<td>$170,750</td>
</tr>
<tr>
<td>Donna Snider</td>
<td><input type="text" value="Dev/Ops"></td>
<td>New York</td>
<td>27</td>
<td>15/01/2022</td>
<td>$112,000</td>
</tr>
</tbody>
</table>
</div>
<script>
$(document).ready(function() {
var table = $('#example').DataTable( {
columnDefs: [
{
targets: [1],
render: function (data, type, row) {
return type === 'export' ? $(data).val() : data;
}
}
],
dom: 'Brftip',
buttons: [ {
text: 'Csv',
extend: 'csvHtml5',
name: 'testExport',
exportOptions: {
columns: [0, 1, 4, 5],
orthogonal: 'export'
}
} ]
} );
} );
</script>
</body>
</html>
The table looks like this:
And the exported CSV data is:
"Name","Position","Start date","Salary"
"Donna Snider","Dev/Ops","15/01/2022","$112,000"
"Garrett Winters","Accountant","14/12/2021","$170,750"
"Tiger Nixon","System Architect","13/11/2021","$320,800"
The benefit: I do not need any potentially error-prone logic to detect whether my strings contain / characters, or begin with <input... and so on.

Hide row on checkbox specyfic column and word datatables

$.fn.dataTable.ext.search.push(
function (settings, searchData, index, rowData, counter) {
var checked = $('input:checkbox[name="chk_box"]').is(':checked');
// If checked and Position column is blank don't display the row
if (checked &&searchData[1] ==='druk') {
return false;
}
// Otherwise display the row
return true;
});
Is it possible to hide a row after searching for a specific string? I have this function, now it is looking for the exact word and how to make it look for the string:
Sorry about that:Now he hides a rows with only the word "druk". I can't do it so that it hides a rows with a sequence of characters, e.g. "abcdruk"
<div class="formRight"> <label> <input class="checkbox" type="checkbox" name="chk_box" checked /> hide</label> </div> <table> <tbody> <tr class="line"> <td>druk1234</td> </tr> <tr class="line"> <td>abcd</td> </tr> <tr class="line"> <td>abcdruk</td> </tr </tr> </tbody> </table>
I tried to use the includes function for example,but I don't know how to apply it to my example:
const s1 = 'javascript';
const s2 = 'python';
console.log(s1.includes('script')); // true
console.log(s2.includes('script')); // false
console.log(s1.contains('java')) // ERROR! .contains is not a function

Get value of hidden column in DataTable

Once a button is selected on my page I need to iterate through a DataTable, find the row with the radio button that's selected and then get the value from a hidden column on that row. I've tried just about everything but my hidden columns aren't accessible, only my 3 visible columns. My code example has several of the options I've tried to I apologize if it's a little messy. This is my first time posting so please don't get mad if I've messed something up here. I've seen the .fnGetData option but it's listed as "legacy" so I'd rather not use it (???).
Table Setup:
var thisurl = '#Url.Action("Addresses", new { AddUID = "000" })';
thisurl = thisurl.replace("000", #ViewBag.AddUID);
$('##ViewBag.TblID').dataTable({
"columnDefs": [
{
"targets": [0, 1],
"visible" : false
}
],
"searching": false,
"info": false,
"paging": false,
"order": [1, "desc"],
"ajax": {
"type": "POST",
"url": thisurl,
"contentType": 'application/json; charset=utf-8',
"data": function (data) { return data = JSON.stringify(data); }
},
"columns": [
{ "data": "Adrs_UID" },
{ "data": "revision_id" },
{
"render": function (data, type, row) {
var url = '#Html.RadioButton("000", "select", false, htmlAttributes: new { #id = "111", onclick = "ResetRadioBtns()" })';
url = url.replace("000", '#ViewBag.ChkID').replace("111", '#ViewBag.ChkID');
return url;
}
},
{
"render": function (data, type, row) {
return row.Adrs_One + ' ' + row.Adrs_City + ' ' + row.Adrs_St + ' ' + row.Adrs_Zip;
}
},
{
"render": function (data, type, row) {
var text = 'Print';
var target = '_blank';
var link = '../DocGen/DocGen_AB.aspx?AUID=' + row.Adrs_UID + '&REV=' + row.revision_id;
return '' + text + '';
}
}
]
});
Script
$('.btnAPL_DTV').click(function (e) {
var table = $('#tblAPLAddress_DTV').DataTable();
table.rows().every(function (value, index) {
node = table.row(value).node();
var check = $(node).find("input[id$='chbxAPLAdSelect_DTV']");
var data1 = $('#tblAPLAddress_DTV').DataTable().row(this).data();
data = data1[0];
if (check.prop("checked") == true) {
// Get Rev_Id & Adrs_UID from Hiddent Field
var allData = table.row(index).data();
var revData = allData[0].data();
adrsUID = node.data()[0];
revId = node.data()[1];
}
});
});
UPDATE 08-13-2021
Andrew - Here the script code and my results from yesterday using your example (including using the index value so you can see what I did yesterday):
$("#tblAPLAddress_DTV").on("click", ":radio", function () {
var table = $('#tblAPLAddress_DTV').DataTable();
var rowNode = $(this).parent().parent()[0];
console.log("Result using .Adrs_UID: " + table.row(rowNode).data().Adrs_UID);
console.log("Result using Index Value: " + table.row(rowNode).data()[0]);
});
And here are my results displayed in the console:
Result using .Adrs_UID: undefined
Result using Index Value: <input id="chbxAPLAdSelect_DTV" name="chbxAPLAdSelect_DTV" onclick="ResetRadioBtns()" type="radio" value="select">
I'm not sure which HTML you're referring to. Here's my HTML table set-up. #ViewBag.TblID = "tblAPLAddress_DTV":
<div class="row">
<div class="col-md-offset-1">
<table id=#ViewBag.TblID class="table text-nowrap" style="padding-top:1em">
<thead>
<tr>
<th>Address UID</th>
<th>RevisionID</th>
<th></th>
<th>Address</th>
<th>Address Block</th>
</tr>
</thead>
</table>
</div>
</div>
And I don't think this matters but I apologize if it does. My table HTML set-up and table definition script above are in a Partial View.
Here's the Html call in the Index View to call the PartialView:
#{ Html.RenderAction("_Addresses", "DocGeneration", new { id = "tblAPLAddress_DTV", chkId = "chbxAPLAdSelect_DTV", AddUID = ViewBag.AddUID });}
Hope this gives you the info you need.
UPDATE 2
Updated script:
$("#tblAPLAddress_DTV").on("click", ":radio", function () {
var table = $('#tblAPLAddress_DTV').DataTable();
var rowNode = $(this).parent().parent()[0];
console.log(rowNode);
});
Console results - I didn't drill down too far.
<tr role="row" class="odd">
<td class="sorting_1">
<input id="chbxAPLAdSelect_DTV" name="chbxAPLAdSelect_DTV" onclick="ResetRadioBtns()" type="radio" value="select">
</td>
<td>17647 157TH STREET BONNER SPRINGS XX 66000</td>
<td>Print</td>
</tr>
These console results are the same as the HTML getting generated.
Thank you!
I have taken a simplified version of your table and code to show one possible approach:
$(document).ready(function() {
var table = $('#example').DataTable( {
"columnDefs": [
{
"targets": [0, 1],
"visible" : false
}
]
} );
$( "#example" ).on( "click", ":radio", function() {
var rowNode = $( this ).parent().parent()[0];
console.log( table.row(rowNode).data()[0] );
});
} );
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
</head>
<body>
<div style="margin: 20px;">
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Position</th>
<th>Office in Country</th>
<th>Age</th>
<th>Start date</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td>System Architect</td>
<td>Edinburgh</td>
<td>61</td>
<td>2011/04/25</td>
<td><input type="radio"></td>
</tr>
<tr>
<td>Garrett Winters</td>
<td>Accountant</td>
<td>Tokyo</td>
<td>63</td>
<td>2011/07/25</td>
<td><input type="radio"></td>
</tr>
<tr>
<td>Ashton Cox</td>
<td>Junior "Technical" Author</td>
<td>San Francisco</td>
<td>66</td>
<td>2009/01/12</td>
<td><input type="radio"></td>
</tr>
<tr>
<td>Cedric Kelly</td>
<td>Senior Javascript Developer</td>
<td>London</td>
<td>22</td>
<td>2012/03/29</td>
<td><input type="radio"></td>
</tr>
<tr>
<td>Airi Satou</td>
<td>Accountant</td>
<td>Milan</td>
<td>33</td>
<td>2008/11/28</td>
<td><input type="radio"></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
(In my case, the radio buttons are not cleared after each click, because I did not add that logic.)
How this works:
I use a delegated event handler:
$( "#example" ).on( "click", ":radio", function() { ... }
This allows events to be attached to radio buttons which may not initially be drawn - for example, if they are not on page 1 of the DataTable's paginated data.
I extract the row node (the <tr> element) using jQuery:
var rowNode = $( this ).parent().parent()[0];
In my case, the row node is the grandparent (.parent().parent()) of the radio button. Your case may be slightly different. I don't know what your final rendered HTML structure is.
In my case, each row of data is stored as an array of values (not as an object), so I can use the following to access the first hidden value:
table.row(rowNode).data()[0]
Note that the table variable was already defined as part of the DataTable declaration.
If your row data is provided as objects { ... } instead of arrays [ ... ], you will need to adjust your code accordingly.
You can log table.row(rowNode).data() to the console to see for yourself. You haven't shown us your JSON data, but it looks as if you would need to access the named values, such as adrsUID - so probably this:
table.row(rowNode).data().adrsUID

Datatables plugin to search for header values

I am using jquery's dataTables plugin. I am having skills value as header in my table. I want to serch for users with specific skills. For example as shown in figure i want to search for users who have skill php. Then i should get the name of John. For css i should get name of Mona. The snapshot is here:
You can do this by implementing a custom filter for the datatable, as described here.
Before filtering you have to find, the index of the column you want to filter and check afterwards the value in each row.
This could look somewhat like this:
<input type="text" id="skillFilter" />
<table id="example">
<thead>
<tr>
<th>Skills</th>
<th>PHP</th>
<th>CSS</th>
<th>HTML</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>1</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>Mona</td>
<td>0</td>
<td>1</td>
<td>0</td>
</tr>
</tbody>
</table>
script :
$.fn.dataTable.ext.search.push(
function(settings, data) {
if (skillFilterColumnIndex != undefined) {
//Get the data of each row
var data = data[skillFilterColumnIndex] || "";
return data > 0;
} else {
return 1;
}
});
$("#skillFilter").change(function() {
var skill = $("#skillFilter").val().toLowerCase();
//Find the column index with the skill to filter
$.each($("#example thead th"), function(index, element) {
if (index != 0 && element.innerHTML.toLowerCase() == skill) {
skillFilterColumnIndex = index;
}
});
table.fnDraw();
});