DataTables footerCallback - conditional on another cell - javascript

I'm trying to implement a footerCallback in DataTables that computes a conditional sum of each column, based on a cell that's in a different column in the same row. Here's a demo of my setup: https://jsfiddle.net/rantoun/552y9j90/13/
HTML:
<table id="table1">
<thead>
<tr>
<th>Fruit</th>
<th>sumCondition</th>
<th># Eaten</th>
<th># Remaining</th>
</tr>
</thead>
<tfoot>
<tr>
<th></th>
<th align="center">Count</th>
<th align="left"></th>
<th align="left"></th>
</tr>
</tfoot>
<tbody>
<tr>
<td>Apples</td>
<td>Use</td>
<td>3</td>
<td>8</td>
</tr>
<tr>
<td>Oranges</td>
<td>Use</td>
<td>6</td>
<td>5</td>
</tr>
<tr>
<td>Bananas</td>
<td>Ignore</td>
<td>2</td>
<td>9</td>
</tr>
</tbody>
</table>
jQuery:
$("#table1").DataTable({
"paging": false,
"searching": false,
"info": false,
"footerCallback": function ( row, data, start, end, display ) {
var columns = [2, 3];
var api = this.api();
_.each(columns, function(idx) {
var total = api
.column(idx)
.data()
.reduce(function (a, b) {
return parseInt(a) + parseInt(b);
}, 0)
$('tr:eq(0) th:eq('+idx+')', api.table().footer()).html(total);
})
}
});
Specifically, my goal is for the footerCallback to only sum the rows where "Ignore" is NOT in the Condition column. Hopefully this is clear and any help is appreciated.

I solved this by getting the current index of the summing value in the reduce function and then using the index to access the respective value in the condition cell. Below is the new jQuery code:
$('#table1').DataTable({
'paging': false,
'searching': false,
'info': false,
'footerCallback': function (row, data, start, end, display) {
var columns = [2, 3];
//console.log(data);
var api = this.api();
// Get sumCondition and put in array
_.each(columns, function (idx) {
var total = api
.column(idx)
.data()
.reduce(function (a, b) {
// Find index of current value for accessing sumCondition value in same row
var cur_index = api.column(idx).data().indexOf(b);
if (api.column(1).data()[cur_index] != 'Ignore') {
return parseInt(a) + parseInt(b);
} else {
return parseInt(a);
}
}, 0);
$('tr:eq(0) th:eq(' + idx + ')', api.table().footer()).html(total);
});
},
});
Working Fiddle: https://jsfiddle.net/rantoun/552y9j90/14/

There is a problem when rows in column have identical numbers. Where "if" statement is not applied. Base on this topic and answer: Jquery Datatable sum conditional footer callback not displaying correct result
HTML:
<table id="table1">
<thead>
<tr>
<th>Fruit</th>
<th>sumCondition</th>
<th># Eaten</th>
<th># Remaining</th>
</tr>
</thead>
<tfoot>
<tr>
<th></th>
<th align="center">Count</th>
<th align="left"></th>
<th align="left"></th>
</tr>
</tfoot>
<tbody>
<tr>
<td>Apples</td>
<td>Use</td>
<td>3</td>
<td>8</td>
</tr>
<tr>
<td>Oranges</td>
<td>Use</td>
<td>6</td>
<td>5</td>
</tr>
<tr>
<td>Bananas</td>
<td>Ignore</td>
<td>6</td>
<td>9</td>
</tr>
</tbody>
</table>
jQuery:
$("#table1").DataTable({
"paging": false,
"searching": false,
"info": false,
"footerCallback": function ( row, data, start, end, display ) {
var columns = [2, 3];
//console.log(data);
var api = this.api();
// Get sumCondition and put in array
_.each(columns, function(idx) {
var total = total = api.data().reduce(function(a, b) {
var prev = 0;
var next = 0;
if (Array.isArray(a)) {
if (a[1] !== "Ignore") {
prev = parseInt(a[idx]);
}
} else {
prev = a;
}
if (b[1] !== "Ignore") {
next = parseInt(b[idx]);
}
return prev + next;
}, 0)
$('tr:eq(0) th:eq('+idx+')', api.table().footer()).html(total);
})
}
});
Working Fiddle: https://jsfiddle.net/79fx6aqg/1/

Related

Date column is not sorted ascending or descending

I have a column for number, births and types. If I want to order by Number or Type form ASC or DESC it works perfectly but if I want to order by Birth it doesn't. Is like if I click on "Birth" column, it order by "Number" column I don't know very well what is happening, I have little experience with Javascript, I'm practicing.
let sortDirection = 1;
$(document).ready(() => {
$('th').each(function (columna) {
$(this).click(function () {
let registros = $('table').find('tbody > tr').get();
registros.sort(function (a, b) {
let valor1 = parseInt($(a).children('td').eq(columna).text().toUpperCase(), 16);
let valor2 = parseInt($(b).children('td').eq(columna).text().toUpperCase(), 16);
return valor1 < valor2 ? -1 : valor1 > valor2 ? 1 : 0
});
if (sortDirection === 1) registros.reverse();
sortDirection *= -1; // change direction
$.each(registros, function (indice, elemento) {
$('tbody').append(elemento);
});
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<table>
<thead>
<tr>
<th>Name</th>
<th>Birth</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2022-09-22</td>
<td>A</td>
</tr>
<tr>
<td>2</td>
<td>2022-09-26</td>
<td>B</td>
</tr>
<tr>
<td>1</td>
<td>2022-09-21</td>
<td>A</td>
</tr>
<tr>
<td>5</td>
<td>2022-09-29 </td>
<td>C</td>
</tr>
<tr>
<td>3</td>
<td>2022-09-27</td>
<td>C</td>
</tr>
<tr>
<td>4</td>
<td>2022-09-30</td>
<td>D</td>
</tr>
</tbody>
</table>
</body>
I found something.
console.log(parseInt("2022-09-22".toUpperCase(), 16));
// 8226
console.log(parseInt("2022-09-26".toUpperCase(), 16));
// 8226
console.log(parseInt("2022".toUpperCase(), 16));
// 8226
console.log(parseInt("-".toUpperCase(), 16));
// NaN
// -----------------------------
// Like this?
// dictionary-order
console.log(
["2022-09-26","2022-09-25","2022-09-27"].sort(
(a, b) => a.localeCompare(
b, 'en', { sensitivity: 'base' })
));
// ['2022-09-25', '2022-09-26', '2022-09-27']
// numeric sort
console.log(
['2000', '11000', '10000', '1000'].sort(
(a, b) => a.localeCompare(
b, undefined, {'numeric': true})
));
// ['1000', '2000', '10000', '11000']
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare
https://stackoverflow.com/a/54218232/11634537

HTML Table Filtering/Selection

I have an HTML table that has some static data and some from MySQL. It is currently filtering properly, what I need help is adding the "yes" and "no" selections to the selection list. These are just test values, they are being read from MySQL. I am unable to figure out what values to insert here to add values from MySQL to the selection list. Any assistance will be appreciated! Thank you
.append($table.find('tbody tr')
<!DOCTYPE html>
<html lang="en">
<link rel="stylesheet" href="/stylesheets/style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<head>
<meta charset="utf-8">
<title>Filter</title>
</head>
<script>
$.ajax({
url: 'http://localhost:7003/getTable',
type: "get",
dataType: "json",
success: function(data) {
drawTable(data);
}
});
function drawTable(data) {
for (var i = 0; i < data.length; i++) {
drawRow(data[i]);
}
}
</script>
</body>
</html>
<table id="myTable" class="table table-striped">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th class="dropdown-header">Age</th>
<th>Email</th>
<th class="dropdown-header">Gender</th>
<th class="dropdown-header">Term</th>
<th class="dropdown-header">Enrolled</th>
</tr>
</thead>
<tbody>
<script>
function drawRow(rowData) {
var row = $("<tr />")
$("#myTable").append(row);
row.append($('<td>' + rowData.County + '</td>'));
row.append($('<td>' + rowData.County + '</td>'));
row.append($('<td>').attr('data-field-name', 'age').text(rowData.County));
row.append($('<td>' + rowData.County + '</td>'));
row.append($('<td data-field-name="gender">' + rowData.County + '</td>'));
row.append($('<td data-field-name="term">' + rowData.County + '</td>'));
row.append($('<td data-field-name="enrolled">' + rowData.County + '</td>'));
}
</script>
<tr>
<td>John</td>
<td>Smith</td>
<td data-field-name="age">15</td>
<td>123</td>
<td data-field-name="gender">Male</td>
<td data-field-name="term">Summer2017</td>
<td data-field-name="enrolled">Fall2018</td>
</tr>
<tr>
<td>Jane</td>
<td>Doe</td>
<td data-field-name="age">16</td>
<td>456</td>
<td data-field-name="gender">Female</td>
<td data-field-name="term">Fall2018</td>
<td data-field-name="enrolled">Fall2019</td>
</tr>
<tr>
<td>Bobby</td>
<td>Adams</td>
<td data-field-name="age">15</td>
<td>789</td>
<td data-field-name="gender">Male</td>
<td data-field-name="term">Spring2019</td>
<td data-field-name="enrolled">Fall2018</td>
</tr>
<tr>
<td>Sarah</td>
<td>Lee</td>
<td data-field-name="age">15</td>
<td>456</td>
<td data-field-name="gender">Female</td>
<td data-field-name="term">Fall2018</td>
<td data-field-name="enrolled">Fall2018</td>
</tr>
</tbody>
</table>
<script>
(function($) {
$.fn.tableFilterHeaders = function(filterFn) {
this.each((index, header) => {
let $header = $(header),
$table = $header.closest('table'),
text = $header.text(),
colIndex = $header.closest('th').index(),
fieldName = $header.attr('data-field-name') || text.toLowerCase(),
$select = $('<select>')
.data('fieldName', fieldName)
.append($('<option>').text(text).val('').prop('disabled', true))
.append($('<option>').text('All').val('all'))
.append($table.find('tbody tr')
.toArray()
.map(tr => {
return $(tr).find(`td:eq(${colIndex})`).text();
})
.filter(text => text.trim().length > 0)
.sort()
.filter((v, i, a) => a.indexOf(v) === i)
.map(text => {
return $('<option>').text(text).val(text);
}));
$header.empty().append($select.val('').on('change', filterFn));
});
};
$.fn.initRowClasses = function(oddCls, evenCls) {
this.find('tbody tr').each(function(i) {
$(this).toggleClass(oddCls, i % 2 == 0).toggleClass(evenCls, i % 2 == 1);
});
};
$.fn.updateRowClasses = function(oddCls, evenCls) {
this.find('tbody tr:visible:even').addClass(oddCls).removeClass(evenCls);
this.find('tbody tr:visible:odd').addClass(evenCls).removeClass(oddCls);
};
})(jQuery);
$('#myTable').initRowClasses('odd', 'even');
$('.dropdown-header').tableFilterHeaders(filterText);
function filterText(e) {
let $filter = $(e.target),
$table = $filter.closest('table'),
$filters = $table.find('.dropdown-header select'),
filterObj = $filters.toArray().reduce((obj, filter) => {
let $filter = $(filter);
return Object.assign(obj, { [$filter.data('fieldName')] : $filter.val() });
}, {});
if ($filter.val() === 'all') {
$filter.val('')
}
$table.find('tbody tr').each(function() {
$(this).toggle($(this).find('td').toArray().every(td => {
let $td = $(td), fieldName = $td.attr('data-field-name');
if (fieldName != null) {
return filterObj[fieldName] === null ||
filterObj[fieldName] === '' ||
filterObj[fieldName] === 'all' ||
filterObj[fieldName] === $td.text();
}
return true;
}));
});
$table.updateRowClasses('odd', 'even');
}
</script>
Not showing yes and no options.
Your filtering logic and odd/even row coloring logic is called BEFORE the data returns from your ajax even though it appears AFTER on the page/code. This is how async methods work.
You need to call the header and coloring logic inside the drawTable() function after, of course, you are done drawing the table... like this:
function drawTable(data) {
for (var i = 0; i < data.length; i++) {
drawRow(data[i]);
}
$('#myTable').initRowClasses('odd', 'even');
$('.dropdown-header').tableFilterHeaders(filterText);
}
Make sure to remove the foloowing 2 lines of code:
$('#myTable').initRowClasses('odd', 'even');
$('.dropdown-header').tableFilterHeaders(filterText);
from your code, wherever elsewhere they appear. Leaving them will not break anything, it will just run multiple times unnecessary.

sorting html table with jquery

I have a table in html, I have set for each td an id that I will need to sort the table with a Jquery code.
Sorting works with the FireFox browser, but with Chrome it does not work ... do you know how to help me?
$(function() {
$(".table-user-th").click(function() {
var o = $(this).hasClass('asc') ? 'desc' : 'asc';
$('.table-user-th').removeClass('asc').removeClass('desc');
$(this).addClass(o);
var colIndex = $(this).prevAll().length;
var tbod = $(this).closest("table").find("tbody");
var rows = tbod.find("tr");
rows.sort(function(a, b) {
var A = $(a).find("td").eq(colIndex).attr('id');;
var B = $(b).find("td").eq(colIndex).attr('id');;
if (!isNaN(A)) A = Number(A);
if (!isNaN(B)) B = Number(B);
return o == 'asc' ? A > B : B > A;
});
$.each(rows, function(index, ele) {
tbod.append(ele);
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table border="1">
<thead>
<tr>
<th class="table-user-th">Firstname</th>
<th class="table-user-th">Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td id="Mark">Mark</td>
<td id="Red">Red</td>
</tr>
<tr>
<td id="Nick">Nick</td>
<td id="Sid">Sid</td>
</tr>
<tr>
<td id="Alex">Alex</td>
<td id="Nirv">Nirv</td>
</tr>
</tbody>
</table>
It seems like there is no purpose of using ids here.
Actually the problem was in you sort function. It should return not just true/false but the numeric difference between two values. As usual it is return -1/0/1
So here I wrote comparator func that does just that. And depending on sort type I just multiply it on -1 or 1.
I've also refactored a little bit your code not to use classes or ids. Using jquery you can use data method that stores data on element by key/value.
$(function() {
function cmp(a,b) {return a < b ? 1 : a > b ? -1 : 0}
$(".sortable-table").on('click', 'th', function() {
var th = $(this);
var colIndex = th.data('column');
if(typeof colIndex === 'undefined') {
return;
}
var sortType = th.data('sort') === 'asc' ? 'desc' : 'asc';
th.data('sort', sortType);
var table = $(this).closest("table");
table.find('thead th').removeClass('asc desc');
th.addClass(sortType);
var tbody = table.find("tbody");
var rows = tbody.find("tr");
rows.sort(function(a, b) {
var A = $(a).find("td").eq(colIndex).text();
var B = $(b).find("td").eq(colIndex).text();
return cmp(A,B) * (sortType === 'asc' ? -1 : 1);
});
$.each(rows, function(index, ele) {
tbody.append(ele);
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table border="1" class="sortable-table">
<thead>
<tr>
<th data-column="0">Firstname</th>
<th data-column="1">Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mark</td>
<td>Red</td>
</tr>
<tr>
<td>Nick</td>
<td>Sid</td>
</tr>
<tr>
<td>Alex</td>
<td>Nirv</td>
</tr>
</tbody>
</table>
Update
Added
var table = $(this).closest("table");
table.find('thead th').removeClass('asc desc');
th.addClass(sortType);

dataTable search individual with Footer callback to sum column currency

I trying to add to table search individual with Footer callback to sum currency but I can't write the correct code.
html
<table id="example">
<thead>
<tr>
<th>Rendering engine</th>
<th>Browser</th>
<th>Platform(s)</th>
<th>Engine version</th>
<th>CSS grade</th>
<th>price</th>
</tr>
</thead>
<tfoot class='footer'>
<tr>
<th>Rendering engine</th>
<th>Browser</th>
<th>Platform(s)</th>
<th>Engine version</th>
<th>CSS grade</th>
<th>price</th>
</tr>
</tfoot>
<tfoot class='custo'>
<tr >
<th colspan=5 style=text-align:right>Total:</th>
<th></th>
</tr>
</tfoot>
<tbody>
<tr>
<td>Trident</td>
<td>Internet Explorer 4.0</td>
<td>Win 95+</td>
<td> 4</td>
<td>X</td>
<td>R$ 1.000,00</td>
</tr>
<tr>
<td>Trident</td>
<td>Internet Explorer 5.0</td>
<td>Win 95+</td>
<td>5</td>
<td>C</td>
<td>R$ 2.000,00</td>
</tr>
<tr>
<td>Trident</td>
<td>Internet Explorer 5.5</td>
<td>Win 95+</td>
<td>5.5</td>
<td>A</td>
<td>R$ 500,00</td>
</tr>
</tbody>
JS
/* add input text for table footer */
$('#example tfoot.footer th').each( function () {
var title = $(this).text();
$(this).html( '<input type=\"text\"/>' );
});
/* add the sum of currency */
var table = $('#example').DataTable({
// code for footerCallback goes here but its not working
'footerCallback': function ( row, data, start, end, display ) {
var api = this.api(), data;
var intVal = function ( i ) {
return typeof i === 'string' ?
i.replace(/[\R$,]/g, '')*1 :
typeof i === 'number' ?
i : 0;
};
total = api
.column( 5 )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
pageTotal = api
.column( 5, { page: 'current'} )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
$( api.column( 5 ).custo() ).html(
'$'+pageTotal +' ( $'+ total +' total)'
);
}
});
/* make the search indvidual when keyup change. */
table.columns().every( function () {
var that = this;
$( 'input', this.footer() ).on( 'keyup change', function () {
if ( that.search() !== this.value ) {
that
.search( this.value )
.draw();
}
});
});
Here the example without the sum currency: http://jsfiddle.net/minamagdy666/e88yrhaj/13/
the example that I want to use for sum currency on that link: enter link description here
Thanks
do this example :
const Table = $('#foo').DataTable({
. . . . . .,
. . . . . .,
drawCallback: function(){
Table.columns(5, {
page: 'current'
}).every(function() {
var sum = this
.data()
.reduce(function(a, b) {
var x = parseFloat(a) || 0;
var y = parseFloat(b) || 0;
return x + y;
}, 0);
console.log(sum);
$(this.footer()).html(sum);
});
}
});
in this case, the column is column number 5

update html table with data from SQL database table in cshtml page

#{
var db = Database.Open("HEMS");
var LoadDevCmd = "SELECT * FROM " + App.UserMeterID;
var DevQuery = db.Query(LoadDevCmd);
foreach (var row in DevQuery)
{
var DevType = row.DeviceType;
var LoadType = row.Device_LoadType;
string Loc = row.Location;
var MAC = row.MACaddress;
var D_Status = row.DeviceStatus;
var myLoc = "";
if (Loc == null)
{
myLoc = "XLoc";
}
else
{
switch (Loc)
{
case "Living Room":
myLoc = "LivingRoom";
break;
case "Bedroom":
myLoc = "Bedroom";
break;
case "Bathroom":
myLoc = "Bathroom";
break;
case "Dining Room":
myLoc = "DiningRoom";
break;
case "Kitchen":
myLoc = "Kitchen";
break;
case "Balcony":
myLoc = "Balcony";
break;
case "Others":
myLoc = "Others";
break;
}
}
}
<body>
<table id="XLoc">
<tr>
<th align="left">Unknown Location</th>
</tr>
</table>
<table id="LivingRoom">
<tr>
<th align="left">Living Room</th>
</tr>
</table>
<table id="Bedroom">
<tr>
<th align="left">Bedroom</th>
</tr>
</table>
<table id="Bathroom">
<tr>
<th align="left">Bathroom</th>
</tr>
</table>
<table id="DiningRoom">
<tr>
<th align="left">Dining Room</th>
</tr>
</table>
<table id="Kitchen">
<tr>
<th align="left">Kitchen</th>
</tr>
</table>
<table id="Balcony">
<tr>
<th align="left">Balcony</th>
</tr>
</table>
<table id="Others">
<tr>
<th align="left">Others</th>
</tr>
</table>
</body>
<script>
function displayDev(id, MAC) {
var table = document.getElementById(id);
var row = table.insertRow(-1);
var cell = row.insertCell(-1);
cell.innerHTML = "MacAddress: " + MAC ;
}
</script>
I'm trying update my html tables in tag as shown above with the script function( displayDev(id, MAC)}. May I know how may I call the script function in the #{}?
I've tried the page.ClientScriptManager.RegisterStartupScript(); but I always get null page which cause the NullReferenceException error, may I know how should I fix it?
Try #Html.Raw("displayDev(" + myLoc + ")")

Categories