I'm fairly new to javascript and jQuery, so please excuse any incorrect verbiage in my problem description.
Background: I have a site where I'm using the Datatables plugin for jQuery to display a live, realtime display of a SQL table. The SQL query is processed in ASP.NET and returned to the client as a JSON object. Right now, I'm only displaying ~500 records.
Once the JSON object has been parsed by the datatables plugin, I perform some conditional formatting of the table cells -- hence there could be some heavy DOM manipulation if I understand that concept correctly. I've read that this can be where a lot of memory leaks can occur, but I also thought that jQuery was pretty solid at cleaning up after itself.
I'm using a setInterval timer to update the datatable periodically such that it displays changes in real time.
My Problem: Memory consumption of my site is out of control. On every AJAX call (~every 2 sec), the memory usage for the page can jump as much as 2MB. To throttle this back, I installed an idle plugin for jQuery to detect when the user is active on the site - and I reduce the AJAX calls to ~every 1 hour when the user is idle. I heard that this can give more space for the browser to perform garbage collection.
What I find is that memory climbs pretty continuously while active, with a slight drop every 4 or 5 calls where it looks like some garbage collection is being performed. Memory does not climb when the AJAX calls are reduced while the user is idle. Below, I've pasted a stripped down version of my code (excluding some irrelevant snippets). I'm sure it's not the cleanest code - but if someone could provide a pointer as to what might be causing the memory consumption -- or how I could reduce the consumption, it would be greatly appreciated.
Thanks in advance!
//TIMER
var updateFreq = 5000;
var timer;
setIdleTimeout(5000); // 5 seconds
setAwayTimeout(50000); // 10 seconds
document.onIdle = function() {
clearInterval(timer);
updateTable(3600000); //update once an hour
//if (typeof (CollectGarbage) == "function") { CollectGarbage(); }
}
document.onAway = function() {
clearInterval(timer);
updateTable(3600000); //update once an hour
//if (typeof (CollectGarbage) == "function") { CollectGarbage(); }
}
document.onBack = function(isIdle, isAway) {
clearInterval(timer);
updateTable(5000); //update once every two seconds
}
//END TIMER
var oTable;
var oSettings;
$(document).ready(function() {
oTable = $("#production_table").dataTable({
"sDom": '<"TT"T><"tab"t><"F"fip>',
"iDisplayLength": -1,
"sAjaxSource": 'Data.ashx',
// "sScrollY": y - 217,
//"sScrollX": "100%",
"bJQueryUI": true,
"bDeferRender": true,
// "bStateSave": true,
"aaSorting": [[16, 'desc']],
"sPaginationType": "full_numbers",
// "bAutoWidth": false,
"fnDrawCallback": function(oSettings) {
addFormat();
try {
$('td').disableTextSelect();
}
catch (err) {
}
},
"fnServerData": function(sSource, aoData, fnCallback) {
$.ajax({
dataType: 'json',
type: "GET",
cache: false,
url: sSource,
data: aoData,
success: fnCallback
})
},
"fnInitComplete": function(oSettings, json) {
//alert('DataTables has finished its initialisation.');
// addFormat();
//$('td').disableTextSelect();
},
// "bProcessing": true,
"aoColumns": [
{ "mDataProp": null, "bSortable": false },
{ "mDataProp": "serial", "sClass": "serial" },
{ "mDataProp": "us", "sClass": "us" },
{ "mDataProp": "type", "sClass": "type" },
{ "mDataProp": "compartment", "sClass": "compartment" },
{ "mDataProp": "leg", "sClass": "leg", "bVisible": false },
{ "mDataProp": "comp", "sClass": "comp", "bVisible": false },
{ "mDataProp": "patient", "sClass": "patient", "bVisible": false },
{ "mDataProp": "dob", "sClass": "dob", "bVisible": false },
{ "mDataProp": "surgeon", "sClass": "surgeon", "bVisible": false },
{ "mDataProp": "surgery", "sClass": "surgery", "bVisible": false }
//I've truncated this section slightly...
]
});
updateTable(updateFreq);
});
function updateTable(uF) {
// Update the table using the fnReloadAjax call method
timer = setInterval(function() {
var iStart = oSettings._iDisplayStart;
oSettings = oTable.fnSettings();
oTable.fnReloadAjax();
//oTable.fnDisplayStart(oSettings, iStart, true);
}, uF);
}
I should have posted this a long while ago. What was causing this to spiral out of control was that I was requesting the entire data set (several thousand rows) every 2 seconds.
What I ended up doing to fix this was changing my data source such that on load, I would get all of the data from the source - but any time after that, I would pull from a different source that only returned changes since the last request. It really cut down on the data being sent down. As a result, memory usage was significantly cut down and garbage collection had no problems handling any slight climbs.
Related
I'm using jquery data table to display large amounts of data inside grid. I implemented server side pagination but I'm struggling with sorting data server side.
Below is my datatable initialization, answer with query for sorting is not the subject here, I just need a way to pass information of which column is clicked to the controller, the one upon I will do the sorting.
('#myTable').dataTable({
"processing": true,
"serverSide": true,
"info": false,
"pageLength": 10,
"lengthChange": false,
"stateSave": false,
"bPaginate": true,
"bFilter": false,
"sPaginationType": "full_numbers",
"info": "Page _PAGE_ from _PAGES_",
"infoEmpty": "No data",
"oPaginate": {
"sFirst": "First",
"sPrevious": "Previous",
"sNext": "Next",
"sLast": "Last"
},
"ajax": {
"url": "#string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~"))/MyController/GetData",
"type": "GET",
"data": function (d) {
.....
},
},
preDrawCallback: function (settings) {
...
},
drawCallback: function (settings) {
...
},
"columns": [
{ "data": "Id" },
{ "data": "FirstName" },
{ "data": "LastName" },
{ "data": "Age" }
],
"columnDefs": [
{
targets: [0],
orderable: false
},
{
render: function (data, type, row) {
...
}
],
"order": [[0, "desc"]]
});
public ActionResult GetData(){
var sortColumn = ...
...
}
You can bind the 'order' event like this:
$('#myTable').on( 'order.dt', function () {
var order = table.order();
var column_selected = order[0][0];
var order_way= order[0][1];
doStuff(column_selected ,order_way);
});
See it in plugin reference
By specifying "serverSide": true,, datatables will by default add information to the request which you need to use in your server-side code. If you look in the Firebug Network panel, you will be able to see the request with the querystring parameters. See here for the full list of parameters. Note that the link is to the v1.9 documentation, because that's what it looks like you're using.
So for sorting, you'd be interested in iSortCol_0 and sSortDir_0 which relate to the clicked column and the sort direction respectively.
In your controller method, you would access the parameters like this:
var sortColumnIndex = Convert.ToInt32(Request["iSortCol_0"]);
var sortColumnDir = Request["sSortDir_0"];
You then need to incorporate this, and the other parameters into your query.
Here is an article on using server-side datatables with MVC
I am using DataTable for retrieving the data from server side. Here. NO information on DataTable. It shows No matching records found error.
Here, oLanguage.sEmptyTable is not working and oLanguage.sZeroRecords is working refer http://datatables.net/ref#sZeroRecords
var DataTableApp = $('#DataTableApp').dataTable({
"sAjaxSource": "php/getAppDetails.php",
"bRetrieve":true,
"bDestroy":true,
"bServerSide": true,
//"bProcessing": true,
"sAjaxDataProp": "aaData",
//"bDeferRender": true,
"sServerMethod": "POST",
"iTotalDisplayRecords":1,
"iTotalRecords":1,
"oLanguage": {
"sZeroRecords": "No records to displays"
},
"fnServerParams": function ( aoData ) {
var imei_app = document.getElementById('imei').value;
console.log(imei_app);
aoData.push({"name":"imei","value":imei_app});
},
//aoColumns
"aoColumns": [{
"mData": "appName"
}, {
"mData": "appId"
}, {
"mData": "versionInstalled"
}, {
"mData": "appSize"
}, {
"mData":"dataSize"
},{
"mData": "appType"
},{
"mData":"installedLocation"
},{
"mData": "installedTime"
}]
});
oLanguage.sEmptyTable and oLanguage.sZeroRecords (or in the latest format language.emptyTable and language.zeroRecords) have different purposes.
language.emptyTable Displays when the table contains no rows at all.
language.zeroRecords Displays when, after applying filters, there are now no records to display.
It sounds like your table had rows before filters were applied.
You need to add .dataTables_empty CSS class with display: none attribute to your global style sheet (i.e. src/styles.css).
Note: in the angular, global style sheet is located at SCSS folder (i.e. scss/_custom.scss).
.dataTables_empty {
display: none;
}
https://l-lin.github.io/angular-datatables/#/basic/server-side-angular-way
Below is the document ready function
$('#example').dataTable({
"bProcessing": true,
"bServerSide": true,
"aaSorting": [[2, "asc"]],
"sAjaxSource": "/userControl/GetUser.php",
"aoColumnDefs": [{
"aTargets": [0],
"mData": "download_link",
"mRender": function (data, type, full) {
return 'Detail<br/>Delete';
}
}],
"aoColumns": [
{ "mData": null },
{ "mData": "LoginId" },
{ "mData": "FirstName" },
{ "mData": "LastName" }
]
});
var oTable = $('#example').dataTable();
oTable.fnSort([1, 'asc']);
With the above code, the datatable was stuck on "Processing..." like the below screen shows, but if I remove the sorting, the data was shown correctly, but whenever the user request a column to be sort, the result was still the same, is there anything that I did wrong?
I removed "bServerSide": true, and the DataTables can sort and filter properly now
Your server-side implementation has to handle sorting via the iSortCol parameters, using fnSort is for client-side implementations and will not work for server-side
As #mainguy said in his comment, removing bServerSide will disable pagination and more than likely search
Look at the examples for asp.net on the site, as thats the lang you tagged, if you need more assistance, update your question with the asp.net source code
Please see if your server response has the same counter value for draw or sEcho property as sent by the client.
Example, request may contain draw: 11 or sEcho: 11 parameters, then, the server response must contain draw: "11" or sEcho: "11".
I'm using the new Data Tables extra plugin - Scroller.
However, I am having trouble when refreshing the existing Data Table Grid with a new data source array from an ajax request. (Code below).
The first time I run this exact code on the page, it works properly. However, whenever I call this code snippet again given a different source, the data table is re-rendered but is missing all column headers. Does anyone know why the Columns are disappearing every subsequent time after the first?
oTable = $('#example').dataTable({
"aoColumns": [
{ "sTitle": "ID" },
{ "sTitle": "Test" },
{ "sTitle": "Type" },
{ "sTitle": "Date" },
{ "sTitle": "Revision" }
],
"aaData": source,
"bDestroy":true,
"sScrollY": "380px",
"sDom": 'frtiS',
"bDeferRender": true,
"fnRowCallback": function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
$(nRow).attr('id', "row-" + aData[0]);
return nRow;
}
});
I have also had the same problem and here is the answer from Allan and it worked perfectly in my case.
http://www.datatables.net/forums/discussion/14278/scroller-plugin-misplaces-datatables-column-header#Item_1
I don't know if it's a bug, but I have a datatable+ajax with the following options:
"bServerSide": true,
"sAjaxSource": url,
"fnServerData": function (sSource, aoData, fnCallback) {
jQuery.ajax({
"dataType": 'json',
"type": "POST",
"url": sSource,
"data": aoData,
"success": fnCallback
});
},
"sPaginationType": "bootstrap",
"aoColumns": [
{ "sName": "Id", "sType": 'numeric', "bVisible": false },
{ "sName": "PostingDate", "sType": 'Date' },
{ "sName": "Userid", "sType": 'string', "bVisible": false },
{ "sName": "DisplayName" },
{ "sName": "Description" },
{ "sName": "MainTag" },
{ "sName": "Tags" },
{ "sName": "HowMuch" }
]
I have a form where users can add rows and when they submit it I add data to the database with an ajax call and then call:
jQuery('#mydatatable').dataTable().fnReloadAjax();
When a user click to sort the table by column "MainTag" my server-side ajax receives:
iSortCol_0 4
iSortingCols 1
And all bSortable_# are there, correctly from 0 to 7 (I have 8 columns as shown above.
Now my problem is iSortCol_0 is misleading, since the columns where hidden, if I don't have a mean to know which columns are hidden on the server I misinterpret iSortCol_0=4 sorting by the wrong column.
I can implement a workaround, sending the information of which columns are displayed or hidden externally to datatables but I have the feeling either I am doing something wrong or I have missed to find the answer to my problem in the documentation.
I don't think that there is an automatic way to know that, what i'd do is send an extra parameter to the server using fnServerParams() (as detailed in this example) to inform the server about what columns are hidden
"fnServerParams": function ( aoData ) {
aoData.push( { "name": "more_data", "value": "my_value" } );
}