datatable, update footer info totalSize value without redrawing table - javascript

I have a datatable that gets called in a function like this:
function createDatatable(){
//get table data
var resp = getTableData()
var dataset = resp.data //table data
var total = resp.total //number like 238
//if table already exists
if (myProductGapsTable) {
myProductGapsTable.clear();
myProductGapsTable.rows.add(dataset); //add new dataset
//myProductGapsTable.language.reload(); //trying to get something like this to work
myProductGapsTable.draw();
} else {
//create table
myProductGapsTable = $('#myProductGapsTable').DataTable({
scrollY: "60vh",
scrollX: true,
scrollCollapse: true,
paging: false,
fixedColumns: true,
"autoWidth": true,
data: dataset,
retrieve: false,
"language": {
"emptyTable": "No table data availiable.",
"info": `Showing _START_ to _END_ of ${total} entries`,
},
"sDom": 'ti',
"paging": false,
"preDrawCallback": function (settings) {
pageScrollPos = $("#myProductGapsTableContainer div.dataTables_scrollBody").scrollTop();
},
"drawCallback": function (settings) {
$("#myProductGapsTableContainer div.dataTables_scrollBody").scrollTop(pageScrollPos);
},
buttons: [
{
extend: 'excelHtml5',
text: 'excel',
exportOptions: { rows: { selected: true, search: 'applied' } }
},
{
extend: 'csvHtml5',
text: 'csv',
exportOptions: { rows: { selected: true, search: 'applied' } }
},
],
select: {
style: 'multi',
selector: 'td:first-child',
search: 'applied'
},
order: [1, 'asc'],
});
}
}
I am trying to have the info field use a custom total number for the info footer, this works fine when the table is first created; it will load the number (238 initially) in the footer correctly. But when I call the function again, if the total number is changed (like now lets say total is 77), the footer info text will not show the updated 'out of 77' text that I would like it to have.
I have an if statement that checks if the table has already been created when the function is called, is there any way I can refresh or reload the table's language field? So I can refresh the table's lower dom info text when the if statement is called?

I don't know of a way to do this using the DataTables API, unfortunately, but here is a jQuery/DOM way:
function changeCountTotal() {
total = resp.total;
var info = $('#myProductGapsTable_info').html();
// Format is assumed to be: "Showing 1 to 10 of 1,234 entries"
var regex = /(Showing.*of ).+?( entries)/;
var updatedinfo = info.replace(regex, "$1" + total + "$2");
$('#example_info').html(updatedinfo);
}
This would need to be called after the redraw peformed by myProductGapsTable.draw();, to ensure the other parameters (_START_, _END_) are correctly re-evaluted by DataTables.
If you want to format the total for thousands separators (or whatever is appropriate for your locale), that would be something like this:
total = resp.total.toLocaleString()
(If there is a way to do this using the DataTables API, that would be a better answer, of course.)

Related

How to load the DataTable jQuery plugin when a Bootstrap modal window is completely loaded without getting asynchronous issues?

I am currently using Bootstrap v4.4.1 and the nodejs bootstrap versions of the plugin:
require('datatables.net-bs4')(window, $);
require('datatables.net-colreorder-bs4')(window, $);
require('datatables.net-fixedheader-bs4')(window, $);
First I load all the rows in the table that is inside the html of the Bootstrap modal window. All these operations are synchronous, so I don't see any issue here. Each one of the functions get_* just add node elements with jQuery.
for (var i = 0; i < cols.length; i++) {
var col_name = cols[i];
var name = self.get_col_name(col_name);
var data_type = self.get_data_type(col_name)
var attrs = self.pj_cols[col_name]['attrs'].join(', '); // TODO: translate to icons or extract just some of them?
var cb_export = self.get_cb_export(i, col_name);
var sel_cur_prec = self.get_cur_prec(col_name);
var txt_cur_unit = self.get_txt_cur_unit(col_name);
var set_bt = self.get_set_bt(col_name, i);
var tr = $('<tr>');
tr.append(
$('<td>', {html: cb_export }),
name,
$('<td>', {html: data_type }),
$('<td>', {text: attrs }),
$('<td>', {html: sel_cur_prec }),
$('<td>', {html: txt_cur_unit }),
$('<td>', {html: set_bt })
);
$('#table_column_project tbody').append(tr);
}
Then I load the modal window programmatically by clicking the button to trigger and show it
$('#modal_column_project').click();
And finally I run the DataTable() function in order to convert the table in a DataTable
$('#column_project_win').on('shown.bs.modal', function (e) {
$('#table_column_project').DataTable( {
scrollY: 400,
scrollCollapse: true,
paging: false,
searching: true,
ordering: true,
order: [[ 1, 'asc' ]],
info: false,
columnDefs: [
{ targets: '_all', visible: true, },
{ targets: [6], orderable: false, searchable: false, },
{ targets: [0, 2, 4, 5, 6], type: 'html'}
],
initComplete: function () {
// initially the div which is a containter has opacity 0
$('#div_column_project').animate({ opacity: 1, }, { duration: 100, });
},
});
});
This is working well just sometimes. Often, the rows never appear and the DataTable shows the message:
No matching records found
Is there a way to do this better in order to avoid this asynchronous issue? I say asynchronous because apparently is random.
I have also tried to add this hack without good results. I think the problem is in the DataTable call where the table rows are removed
var _check_loaded_rows = setInterval(function() {
if ($('#table_column_project tbody tr').length == cols.length) { // check if all rows are correctly loaded
clearInterval(_check_loaded_rows);
$('#table_column_project').DataTable( { // TODO: show only when rendered
scrollY: 400,
scrollCollapse: true,
paging: false,
searching: true,
ordering: true,
order: [[ 1, 'asc' ]], // this is the value by default
info: false,
columnDefs: [
{ targets: '_all', visible: true, },
{ targets: [6], orderable: false, searchable: false, },
{ targets: [0, 2, 4, 5, 6], type: 'html'}
],
initComplete: function () {
$('#div_column_project').animate({ opacity: 1, }, { duration: 100, });
},
});
}
}, 100);
Actually, now I have a setTimeout with 500ms of delay to make it work well.
I have also read this answer where the poster recommends that we should use the bootstrap version, the table should be initialized (I use opacity to hide it, instead of display: none;) and to show the DataTable I use the builtin method initComplete to run the instruction to set the opacity.
If you just have a few hundred rows, why not insert them using the API in initComplete? You can pass the API to another function (like the one using get_* methods you already use to populate (it is unclear if it is a function or not)) :
function populate(api) {
for (var i = 0; i < cols.length; i++) {
...
api.row.add([cb_export, name, data_type, attrs, sel_cur_prec, txt_cur_unit, set_bt])
}
api.draw/()
}
$('#table_column_project').DataTable({
...
initComplete: function() {
const api = this.api()
populate(api)
}
})
Or, you can use a promise and wait for that inside the modal event handler:
function populate() {
return new Promise(resolve => {
...
if (i+1 === cols.length) resolve()
})
}
$('#column_project_win').on('shown.bs.modal', function (e) {
populate().then(() => {
$('#table_column_project').DataTable( {
...
})
})
})
If you not have Promise available in your nodejs (older versions) you can use for example es6-promise and
const Promise = require('es6-promise').Promise

Get Dynamic Filename and Title for Export button in Datatable

I am trying to init different tables with single DataTable initialization code block in which I have created Datatable Excel Button to export data. and I got success pretty much. But now challenge is that I am not able to export data with correct file name.
Actually when datatable initialized, Export button is automatically bind with the table do data is being exported correctly but while assigning file name I am trying to get visible Datatable instance by searching by its class. I am able to get correct table when there is only one table in the page. but when there are multiple table on single page then fining visible table does not give correct table. How can I get correct table instance in my JS code so that it works exactly on correct table.
This is how I am trying to get my visible datatable $('.dt-table:visible').data('excel-filename')
Please let me know how can I get more specific table using below code.
JS CODE
var tableObj = $('.dt-table').DataTable({
retrieve: true,
"lengthMenu": [ [10, 25, 50, -1], [10, 25, 50, 'All'] ],
"language": {
"emptyTable": "No data available to show...",
"info": "Showing _START_ to _END_ from _TOTAL_ records",
"infoEmpty": "0 records to show...",
"lengthMenu": "Show _MENU_ records",
"loadingRecords": "Loading...",
"processing": "Processing...",
"zeroRecords": "No matching records found...",
"infoFiltered": "(filtered from _MAX_ total records)"
},
dom: "<'row'<'col-sm-12'B>>" +
"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
buttons: {
buttons: [
{
text: '<i class="far fa-file-excel pr-2"></i> Export to Excel(.xlsx)',
title: function(thead, data, start, end, display) {
return $('.dt-table:visible').data('excel-title');
},
extend: 'excel',
autoFilter: true,
filename: function() {
var d = new Date($.now());
var n = d.getDate()+"_"+(d.getMonth()+1)+"_"+d.getFullYear()+"_"+d.getHours()+"_"+d.getMinutes()+"_"+d.getSeconds();
return $('.dt-table:visible').data('excel-filename') + '_' + n;
},
customize: function(xlsx) {
var sheet = xlsx.xl.worksheets['sheet1.xml'];
$( 'sheets sheet', xlsx.xl['workbook.xml'] ).attr( 'name', $('.dt-table:visible').data('excel-title') );
},
exportOptions: {
//columns: [ 1, 2, 3 ]
format: {
body: function (data, row, column, node) {
if($(node).find(".notExportable").length) {
return $(data).remove(".notExportable").html();
} else {
return data;
}
}
},
columns: ':not(.notExportable)'
}
}
],
dom: {
container: {
tag: "div",
className: "mb-2 mlmt-act dt-buttons"
},
button: {
tag: "a",
className: "btn btn-info mlmt-button"
},
buttonLiner: {
tag: null
}
}
},
drawCallback: function() {
var hasRows = this.api().rows({ filter: 'applied' }).data().length > 0;
var tableId = this.api().tables().nodes().to$().attr('id');
var excelButton = $('a.mlmt-button[aria-controls="'+tableId+'"]');
//alert(tableId);
//alert('.mlmt-button-'+($('.dt-table:visible').attr('id')));
if(hasRows > 0) {
excelButton.removeAttr('style');
} else {
excelButton.css('pointer-events', 'none').css('background-color', 'gray');
}
}
});
HTML CODE
<table class="table table-bordered table-hover table-striped dt-table" id="tblAllLic" data-page-length='10' data-order='[[0, "asc"]]' data-excel-title="All License List" data-excel-filename="All_Licenses">
</table>

Datatable rowCallback function after initialize

I have the below working code for data table to set cell color based on condition.
$(document).ready(function() {
// DataTable
var table = $('#example').DataTable({
/*
dom: 'Bfrtip',
buttons: ['excel',{
extend: 'pdfHtml5',
orientation: 'landscape',
pageSize: 'LEGAL' }],
"ordering": false,
language: {
search: "_INPUT_",
searchPlaceholder: "Search All Data"
} */
rowCallback: function(row, data, index) {
console.log(row)
if (data[12].split(';')[1] == "In Progress") {
$(row).find('td:eq(11)').addClass('color')
}
}
});
});
The above code works fine but if I remove the comments section which is to add export function, it doesn't work.
I tried reproducing the same issue you're facing. I've created a jsfiddle example, which is working nice and fine. The only thing I changed in your code is that I added a comma (,) before rowCallback, this way :
$('#example').DataTable({
dom: 'Bfrtip',
buttons: ['excel',{
extend: 'pdfHtml5',
orientation: 'landscape',
pageSize: 'LEGAL' }],
"ordering": false,
language: {
search: "_INPUT_",
searchPlaceholder: "Search All Data"
},
rowCallback: function(row, data, index) {
console.log(row)
if (data[12].split(';')[0] == "In Progress") {
$(row).find('td:eq(11)').addClass('color')
}
}
});
and Plus, do recheck that the files related to jQuery datatable are same as the files I've added in my jsfiddle , there is a possibility that you've added a file twice or you might be using an older version of Jquery. If the issue still persists, edit your question and add your html + the files related to jquery that you've initialized.

jquery Datatable manipulation causing the entire page to reload

I have a jQuery datatable that is initially populated using ajax call, and when i click anywhere on the table like pagination number or display length dropdown list, the whole page is reloaded indefinetely. Here is how i populate the datatable.
let table = $('#data-table').DataTable();
function populateTable(){
table = $('#data-table').DataTable({
destroy: true,
responsive: true,
serverSide: false,
autoWidth: false,
paging: true,
filter: true,
searching: true,
stateSave: true,
scrollX: true,
lengthMenu: [10, 25, 50, 75, 100],
language: {
"search": "Filtrer: "
},
ajax: {
url: '/Observer/GetActiveClientsByFloor',
type: 'POST',
data: {
FloorId: floorId,
Type: type
},
dataSrc: ''
},
columns: [
{
title: 'Zone',
data: 'LastKnownZone',
},
{
title: 'HiƩrarchie Map',
data: 'MapInfo.mapHierarchyString',
},
{
title: 'Addresse MAC',
data: 'macAddress',
},
{
title: 'SSID',
data: 'ssId',
},
],
createdRow: (row, data, dataIndex, cells) => {
const selectedRowProfileId = $('#selectedRowProfileId', window.parent.document).val();
if (selectedRowProfileId !== '') {
if (data['ProfileId'] === selectedRowProfileId) {
$(row).addClass('selectedCustom');
}
}
},
initComplete: function (settings, json)
{
const response = json;
//Show the respone on other part of the page
}
}).order([[1, 'asc']]).draw(false);
}
I would like to know what could be causing page re-load and also know how to make pagination works.
You don't need to call order([[1, 'asc']]).draw(false) after table initialization, just add
order: [[1, 'asc']]
to your table properties, like this
$(document).ready(function(){
let table = $('#data-table').DataTable({
order: [[1, 'asc']],
//Other properties
});
As you are not using server-side DataTables will make pagination automatically when you click pagination buttons, considering that all data has already been loaded in the first Ajax call, but when serverSide is set to true every time you change the pagination a new Ajax call will be made by datatables sending aditional parameters for pagination, ordering etc and you will need to change you backend query, filters and pagination logic based on that params.
Edit:
Also destroy: true is not needed in your case, as Documentation says:
"Destroy any existing table matching the selector and replace with the new options."
You are not re-creating or replacing your table, so you can just remove it

How to show just the N first number of rows on DataTables? How to download all data after this?

I am trying to show only the first N rows of data on DataTables, but can't find a way.
Additionally, when I click the Copy or Excel buttons I want to download all the data, and not just the rows who are being show.
In my last try, I used paging and pageLength without success. Below is the code. My data is on tbldata:
var dtable = $("#dvTableAC").DataTable({
data: tbldata,
columns: [
{ title: "A" },
{ title: "B" },
{ title: "C" },
{ title: "D" }
],
"paging": false,
"pageLength": 50,
dom: 'Blfrtip',
buttons: [
'excel', 'copy'
]
});
Please not that you need an extra plugin to be able to use the buttons (excel, copy).
https://datatables.net/extensions/buttons/built-in
var dtable = $("#dvTableAC").DataTable({
data: tbldata,
columns: [
{ title: "A" },
{ title: "B" },
{ title: "C" },
{ title: "D" }
],
"paging": true,
"pageLenght":10,
dom: 'Blfrtip',
buttons: [
'excel', 'copy'
]
});
Datatables will show all the data you send to it, if you set paging to false, then pageLenght is not used. If you want to limit the total records that datatables show, you must send just those records to it. You can restrict the number on the mysql query using limit 10. But I don't know any method of not having pagination and show only a x amount of rows from the total.

Categories

Resources