I have a datatable that is using standard features (pagination, sorting, searching, date range, etc.), but I also need to have a portion at the bottom of the table that displays the total salary of each office. The output would (ideally) look something like this if you searched for, say, "engineer":
London: $295,500
San Francisco: $409,350
Singapore: $234,500
Tokyo: $139,575
Edinburgh: $103,600
New York: $125,250
Total Hours: $1,307,775.00
I have tried a handful of different approaches, but frankly my script knowledge is lacking and I am out of my depth. Can anyone point me in the right direction on how to solve this issue?
Here is my script:
"footerCallback": function (row, start, end, display) {
var api = this.api(),
data;
// Remove the formatting to get integer data for summation
var intVal = function (i) {
return typeof i === 'string' ?
i.replace(/[\$,]/g, '') * 1 :
typeof i === 'number' ?
i : 0;
};
// ************NOT WORKING************ \\
// Total by category
// First Attempt
if (api.column(5,
{
search: 'applied'
})
.data()
.length) {
var byCategory = api
.rows()
.data()
.reduce(function (a, c) {
a[c[7]] = a[c[7]] || 0;
a[c[7]] += intVal(c[5]);
return a;
},
{});
}
else {
byCategory = 0;
}
console.clear();
console.dir('by category', byCategory);
/*
// Second Attempt
if (api.column(5, {
search: 'applied'
}).data().length) {
var byCategory = api
.rows(5, {
search: 'applied'
})
.data()
.reduce(function (category, hours) {
category[hours[7]] = category[hours[7]] || 0;
category[hours[7]] += intVal(hours[5]);
return category;
}, {});
}
else {
byCategory = 0;
}
console.clear();
console.dir('by category', byCategory); */
// ************NOT WORKING************ \\
// Third Attempt
/*var byCategory = api
.rows()
.data()
.reduce(function (a, c) {
a[c[7]] = a[c[7]] || 0;
a[c[7]] += intVal(c[5]);
for (var key in byCategory) {
if (byCategory.hasOwnProperty(key)) {
console.log(key + " -> " + byCategory[key]);
}
}
}, {}); */
// Total over all pages
total = api
.column(5)
.data()
.reduce(function (a, b) {
return intVal(a) + intVal(b);
}, 0);
// Total over all filtered pages
if (api.column(5, {
search: 'applied'
}).data().length) {
pageTotal = api
.column(5, {
search: 'applied'
})
.data()
.reduce(function (a, b) {
return intVal(a) + intVal(b);
});
} else {
pageTotal = 0;
}
// Update footer
$(api.column(5).footer()).html(
pageTotal.toFixed(2) + ' hours ( ' + total.toFixed(2) + ' total hours)' + '<br>' + Object.entries(byCategory) + ' hours'
//pageTotal.toFixed(2) + ' hours ( ' + total.toFixed(2) + ' total hours)' + '<br>' + Object.keys(byCategory).map(key => { console.log(key, byCategory[key]) }) + ' hours'
//pageTotal.toFixed(2) + ' hours ( ' + total.toFixed(2) + ' total hours)' + '<br>' + Object.keys(byCategory).forEach(key => { console.log(key, byCategory[key]) }) + ' hours'
);
}
Here is a link to my jsfiddle: https://jsfiddle.net/l337method/hfyo90w7/
You Can do sum of salary by office using below code sample as said over here, which you can modify according to your need.
Replace 1 by column number you want to compare data with.
total = api.cells( function ( index, data, node ) {
return api.row( index ).data()[1] === 'textA' ?
true : false;
}, 0 )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
} );
To be more specific : you can do something like this, you can use this function to sum up values. see #davidkonrad for exactly what you want with filter.
$("#example").on('search.dt', function() {
alert(table.column( 0, {page:'current'} ).data().sum() );
});
You should really consider using the small sum() plug-in. In your case, all what you need next is something like
drawCallback: function() {
var sum = this.api().column( 5, { search:'applied', page: 'all' }).data().sum();
$( this.api().column(5).footer() ).text(
'$'+Number(sum.toFixed(1)).toLocaleString()
);
}
You can translate this into "get the sum of all column(5) values from all pages, but only those which is not filtered out". drawCallback will be triggered each and every time you search, filter etc.
Here is a forked version of your fiddle -> https://jsfiddle.net/7cjL35dr/
The code was a little bit confusing so I tried to cleanup before I added the sum() plugin and the drawCallback.
Related
I received data from socket and I need to live sort this data and display.
My code looke like this but only display unsort data.
$.each( data.players, function( key, value ) {
var p = value.value[0]/1000;
$('<tr>'+
'<td>'+
'<p class="price">$'+ p.toFixed(2)+'</p>'+
'</td>'+
'</tr>').appendTo('#target').hide().fadeIn(700);
});
Second problem:
When data was sorted and displayed and socket receive new player, it shoud be added on right place.
Data to sort (each object with id have price atrubute):
data to sort
Try the following
var data = {};
// simulate async data retrieval from socket
setInterval(function() {
// random data
data[parseInt(getRandom(1, 10000))] = {};
// update dome when new data is received
updateDom();
}, getRandom(1000, 5000));
// call this method each time new data is received
updateDom();
function updateDom() {
var keys = [];
// add object keys to an array to be able to sort them
for (k in data) {
if (data.hasOwnProperty(k)) {
keys.push(parseInt(k));
}
}
keys.sort(function(a, b) {
// sort values
return (a < b) ? -1 : ((a > b) ? 1 : 0);
}).forEach(function(e) {
if ($('#target [key=' + e + ']').length === 0) {
// if we haven't printed this yet
// generate dom
var domE =
'<tr key="' + e + '"><td>' +
'<p class="price">$' + e + '</p>' +
'</td></tr>';
// get value of previous key
var prevKey = keys.indexOf(e) - 1;
prevKey = prevKey < 0 ? 0 : prevKey;
// get previous element
var keyBefore = $('#target [key=' + keys[prevKey] + ']');
// if this does not have previous key exchange html
if (keyBefore.length === 0) {
$('#target').html(domE);
} else {
keyBefore
.after(domE)
.hide()
.fadeIn(700);
}
}
});
}
// utils - ignore for real implementation
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="target">Data here!</div>
I'm trying to do something with JavaScript DataTable but I'm stuck somewhere .
There are 2 DataTable on the same page .
I am sending data from the first DataTable second DataTable . I can write in the footer section that collects in the second DataTable int .
I collect information on hours in a second column in the DataTable I want to write a footer . I could not do it.
How do you think I should proceed . Existing employees script below.
Thank you to everyone.
<script type="text/javascript" language="javascript">
$(document).ready(function() {
var t = $('#FlowList').DataTable({
rowReorder: true,
"footerCallback": function ( row, data, start, end, display ) {
var api = this.api(), data;
// Remove the formatting to get integer data for summation
var intVal = function ( i ) {
return typeof i === 'string' ?
i.replace(/[\$,]/g, '')*1 :
typeof i === 'number' ?
i : 0;
};
// Tüm sayfaların toplamı
total = api
.column( 3 )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
// Gösterilen sayfanın toplamı
pageTotal = api
.column( 3, { page: 'current'} )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
// Footer Güncelleme
$( api.column( 3 ).footer() ).html(
/* '$'+pageTotal +' ( $'+ total +' Toplam)' */
'Toplam ' + total
);
}
});
var counter = 1;
var table = $('#NewsListTable').DataTable();
$('#NewsListTable tbody').on('dblclick', 'tr', function(){
var data = table.row( this ).data();
//alert(data[3]);
t.row.add( [
counter +'', //Sıra numarasını her seferinde 1 arttırıyoruz.
data[1],
data[2],
data[3],
data[4],
data[5],
data[6],
data[7],
data[8]
]
).draw( false );
counter++;
});
});
</script>
I want to remove the columns which have total = 0.
So I've tried in different ways.
First, I assigned ID to all columns, for example; every <td> is of column will have their ID eg: First columns <td ID = 'col_1'> , second column all <td ID = 'col_2'> etc. And then in when footer callback I've tried to remove if this column total is ZERO then this $("col_"+i).remove(); this code removed table headers only so I've tried again with $("col_"+i).empty() but again it's just empty. <th> only
Then I've tried to hide the columns by creating dynamic but I don't get any values.
"footerCallback": function ( row, data, start, end, display )
{
var api = this.api(), data;
var intVal = function ( i ) { return typeof i === 'string' ? i.replace(/[\$,]/g, '')*1 : typeof i === 'number' ? i : 0;};
var col_gonna_invis = '[';
for(i=1;i<length_of_coloumns;i++)
{
total_salary = api.column( i ).data().reduce( function (a, b) {return intVal(a) + intVal(b);},0 );
$('#total_cont_'+i).html(total_salary);
if(total_salary == 0)
{
col_gonna_invis += '{"targets": [ '+i+' ], "visible": false, "searchable": false },';
}
}
col_gonna_invis += ']';alert(col_gonna_invis);
},
"aoColumnDefs": col_gonna_invis;
Please someone help me fix this issue or please someone tell me how to hide or remove columns which footer total is 0.
Thank you in advance.
I will suggest you use the visible() API method along with the sum() plugin :
Enhance the API with a column().sum() method :
jQuery.fn.dataTable.Api.register( 'sum()', function ( ) {
return this.flatten().reduce( function ( a, b ) {
if ( typeof a === 'string' ) {
a = a.replace(/[^\d.-]/g, '') * 1;
}
if ( typeof b === 'string' ) {
b = b.replace(/[^\d.-]/g, '') * 1;
}
return a + b;
}, 0 );
} );
now, in initComplete() you can very easy hide columns which have a total or sum() of 0 :
var table = $('#example').dataTable({
//...
initComplete : function() {
var api = this.api(),
colCount = api.row(0).data().length;
for (var i=0; i<colCount; i++) {
if (api.column(i).data().sum() == 0) {
api.column(i).visible(false);
}
}
}
});
demo -> http://jsfiddle.net/qer7e5os/
I have this piece of code (it is part of a large javascript code) and I want it to return 'article' or 'articles' as well as the quantity.
If the var quantity = 1, it should return '1 article'.
If the var quantity = 0 or any other number, it should return 'x articles'
quantity: function () {
var quantity = 0;
simpleCart.each(function (item) {
quantity += item.quantity();
});
return quantity;
},
Can someone please show me how to do this?
Thanks in advance.
A simply ternary would work:
quantity: function () {
var quantity = 0;
simpleCart.each(function (item) {
quantity += item.quantity();
});
return quantity + (quantity == 1 ? ' Article' : ' Articles');
};
As you've said you're new to JavaScript, the ?: operator is basically a compact form of if in the form expression ? when-true : when-false.
quantity: function () {
var quantity = 0;
simpleCart.each(function (item) {
quantity += item.quantity();
});
return (quantity == 1) ? '1 article' : $quantity + ' articles';
}
return quantity==undefined?'':quantity==1?'1 Article':quantity+' Articles';
I am trying to sort a column in a table via the DataTables pluing that has a UK date and time like this: 21/09/2013 11:15
Using the code from Ronan Guilloux:
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
"uk_date-pre": function ( a ) {
if ($.trim(a) != '') {
var frDatea = $.trim(a).split(' ');
var frTimea = frDatea[1].split(':');
var frDatea2 = frDatea[0].split('/');
var x = (frDatea2[2] + frDatea2[1] + frDatea2[0] + frTimea[0] + frTimea[1] + frTimea[2]) * 1;
} else {
var x = 10000000000000; // = l'an 1000 ...
}
return x;
},
"uk_date-asc": function ( a, b ) {
return a - b;
},
"uk_date-desc": function ( a, b ) {
return b - a;
}
} );
and also ive added this code to detect it automatically so i don't have to set which column it is for:
jQuery.fn.dataTableExt.aTypes.unshift(
function ( sData )
{
if (sData !== null && sData.match(/(0[1-9]|[12]\d|3[0-2])\/(0[1-9]|1[0-2])\/\d{4} (0[1-9]|1\d|2[0-3]):(0[1-9]|[1-5]\d)$/))
{
//console.log(sData);
return 'uk_date';
}
return null;
}
);
The problem i have is that although i can see the regex is matching the string it is not then calling the 'uk_date-pre', 'uk_date-asc' or 'uk_date-desc' can anyone explain why it is not working?
After playing with it for some time i had to abandon the regEx and i simply added this to the set up:
aoColumnDefs: [{ "sType": "datetime-uk", "aTargets": [whichCol] }]
I then set the whichCol var to either null or a column number if it was on the page that needed this UK sorting.
For anyone stumbling on this.
The code from Ronan Guilloux will work as expected if you change :
var x = (frDatea2[2] + frDatea2[1] + frDatea2[0] + frTimea[0] + frTimea[1] + frTimea[2]) * 1;
to:
var x = (frDatea2[2] + frDatea2[1] + frDatea2[0] + frTimea[0] + frTimea[1]) * 1;
The reason being that we are dealing with "21/09/2013 11:15" therefore
var frTimea = frDatea[1].split(':');
will only populate frTimea[0] and frTimea[1]...