I have a DataTable and I'm trying to add filters to multiple columns in it. Some columns are strings and require text inputs while others are numbers and require range inputs. I add filters to each column as described here. For the range inputs, I'm trying to add my own custom search plugin, as described here. Essentially, I'm trying to combine strategies from both parts of the documentation: I want to loop through each column applying filters, and for those columns that are numerical, I want to employ range filters. The example table they provide in their multi-column filtering documentation includes numerical columns, but the filters they use for those columns are text inputs, which, frankly, doesn't seem like the way most would go about it in real-world implementations. The documentation they provide for setting up numerical range filters includes an example that only has one column to be filtered by the input, which allows them to hard-code the appropriate column index into their custom function.
My problem is that I don't know how to get the variables that I need into the custom range filter function. Specifically, I need to get the column index and the user inputs into the function. I'm using this bootstrap slider for the range inputs, so in order to get the user input values, I call .slider
on my input and pass in 'getValue'
.
How do I get my variables, specifically the column index and the user inputs, into my custom range filter function?
My code is below. I also made this JSFiddle that shows what I'm trying to do. Notice how both text inputs work if you comment out the custom DataTables function.
function populateEntryTable() {
$('#entryTableContainer').empty();
/* put demo data in array of objects that is used to populate DataTable */
var entries = [{name: John, age: 20, salary: 40000},
{name: Bill, age: 40, salary: 200000},
{name: Amy, age: 31, salary: 150000}];
/*build my table*/
$('#entryTableContainer').append('<table id="entryTable"><thead><tr></tr></thead><tbody></tbody></table>');
for (var key in entries[0]) {
$('#entryTableContainer thead tr').append('<th>' + key + '</th>');
}
for (var i = entries.length - 1; i >= 0; i--) {
for (var key in entries[i]) {
$('#entryTableContainer tbody tr:last-child').append('<td>' + entries[i][key] + '</td>');
}
}
/* add column filters below each column, as described in DataTables documentation */
$('#entryTable thead tr').clone(true).appendTo('#entryTable thead');
var numberInputs = ['age','salary'];
$('#entryTable thead tr:eq(1) th').each(function(i) {
var title = $(this).text();
/* if the col requires a text input filter, do text input filter stuff, which works fine. Else if it requires a number range filter, do number filter stuff, which doesn't work fine. */
if (numberInputs.indexOf(title) == -1) {
$(this).html('<input type="text" placeholder="Search">');
$('input',this).on('keyup change',function() {
if (entryTable.column(i).search() !== this.value) {
entryTable.column(i).search(this.value).draw();
}
});
} else if (numberInputs.indexOf(title) > -1) {
/* get min and max values in each column to set appropriate bootstrap-slider attributes */
var min;
var max;
$('#entryTable tbody tr').each(function(j) {
var item = parseFloat($('#entryTable tbody tr:eq(' + j + ') td:eq(' + i + ')').text());
if (min == undefined || item < min) {
min = Math.floor(item);
}
if (max == undefined || item > max) {
max = Math.ceil(item);
}
});
/* create bootstrap-slider with double inputs */
$(this).html('<input id="' + title + '" data-slider-min="' + min + '" data-slider-max="' + max + '" data-slider-step="1" data-slider-value="[' + min + ',' + max + ']">');
$('#' + title).slider({});
/* add listener for bootstrap-slider change */
$('input',this).on('change',function() {
/* returns an array with the min and max user inputs*/
var userInputs = $(this).slider('getValue');
var userMin = userInputs[0];
var userMax = userInputs[1];
entryTable.draw();
});
}
});
/* call DataTable on my table and include my option settings*/
var entryTable = $('#entryTable').DataTable({
orderCellsTop: true,
paging: false,
bInfo: false,
scrollY: 400,
scrollCollapse: true,
order: [ 1, 'desc' ],
searching: true
});
/* searching must be set to true for my column searches to work, but I don't want the whole table search bar to display, so I remove it here */
$('#entryTable_filter').addClass('d-none');
}
// custom DataTables function for filtering number ranges
$.fn.dataTable.ext.search.push(
function( settings, data, dataIndex ) {
/* how do I get i (the col index of the filter that the user is engaging with), the userMin and the userMax into here??? */
var colVal = parseFloat(data[i].replace('$','')) || 0;
if ( ( isNaN( userMin ) && isNaN( userMax ) ) ||
( isNaN( userMin ) && colVal <= userMax ) ||
( userMin <= colVal && isNaN( userMax ) ) ||
( userMin <= colVal && colVal <= userMax ) )
{
return true;
}
return false;
}
);
I was able to solve my problem by moving the custom function inside the .each loop, bringing the variables I need into the proper scope. I put my solution on JSFiddle.