bStateSave and pagination
bStateSave and pagination
Hi,
I have a bit of a problem and I was wondering if you could help me.
My table data comes from the server side through ajax and pagination, sorting etc. are done on the server side. I was asked for two features: one, that if the user refreshes the page, he's not taken back to page 1, and two, that the table is auto-refreshed every 30 seconds (the table displays a lists of incoming messages, so naturally they want new messages to appear automatically). Easy enough, right?
I use bStateSave for the first feature.
I use the following code for the second (it works fine):
[code]var timer = setInterval(function(){ oTable.fnDraw(); }, 30000);[/code]
But now I have two problems. Let's say I have 12 rows and display 10 rows per page:
1) If I select page 2, and refresh through the browser, page 2 is indeed pre-selected, but instead of displaying "Showing 11 to 12 of total 12 entries" and displaying the last 2 rows, it says, "Showing 11 to 20 of total 12 entries" (not very logical) and it shows the first 10 results from page 1. Wha..? o_O
2) The auto-refresh basically resets to page 1 every single time, thus contradicting the bStateSave feature. All other "remembered" settings from the cookie work: if I change the number of displayed rows to 25, it keeps it in memory after the auto-refresh. But it always goes back to page 1. I know it's a bit silly to have an auto-refresh and not see the latest messages at the top (since naturally the messages are sorted by date), but that's what they asked, so... why would just calling fnDraw() reset iDisplayStart to 0? Is there a way I can force it to stay on the current page?
Thanks for any help. Here is my full table code, in case you see anything in there that might explain the problem (the fnServerData is there so that some custom filters from a form are taken into account whenever the table is redrawn):
[code]
var oTable = $('#messagesAntenne').dataTable( {
"bJQueryUI": true,
"bStateSave": true,
"iDisplayLength": 10,
"bLengthChange": false,
"sPaginationType": "full_numbers",
"bProcessing": true,
"bFilter": false,
"bLengthChange" : true,
"bServerSide" : true,
"bAutoWidth": false,
"sAjaxSource" : "index.php?option=com_cfmmessages&task=listmsg&format=json",
"fnServerData" : function (sSource, aoData, fnCallback) {
aoData.push({'name' : 'salon', 'value' : $('#salon').val()});
aoData.push({'name' : 'user', 'value' : $('#userFilter').val()});
aoData.push({'name' : 'dateFrom', 'value' : $('#dateFrom').val()});
aoData.push({'name' : 'dateTo', 'value' : $('#dateTo').val()});
$('input[name=statusFilter[]]').each(function(i) {
aoData.push({'name' : $(this).val(), 'value' : $(this).is(':checked')});
});
$.getJSON( sSource, aoData, function (json) {
fnCallback(json)
} );
}
} );
[/code]
I have a bit of a problem and I was wondering if you could help me.
My table data comes from the server side through ajax and pagination, sorting etc. are done on the server side. I was asked for two features: one, that if the user refreshes the page, he's not taken back to page 1, and two, that the table is auto-refreshed every 30 seconds (the table displays a lists of incoming messages, so naturally they want new messages to appear automatically). Easy enough, right?
I use bStateSave for the first feature.
I use the following code for the second (it works fine):
[code]var timer = setInterval(function(){ oTable.fnDraw(); }, 30000);[/code]
But now I have two problems. Let's say I have 12 rows and display 10 rows per page:
1) If I select page 2, and refresh through the browser, page 2 is indeed pre-selected, but instead of displaying "Showing 11 to 12 of total 12 entries" and displaying the last 2 rows, it says, "Showing 11 to 20 of total 12 entries" (not very logical) and it shows the first 10 results from page 1. Wha..? o_O
2) The auto-refresh basically resets to page 1 every single time, thus contradicting the bStateSave feature. All other "remembered" settings from the cookie work: if I change the number of displayed rows to 25, it keeps it in memory after the auto-refresh. But it always goes back to page 1. I know it's a bit silly to have an auto-refresh and not see the latest messages at the top (since naturally the messages are sorted by date), but that's what they asked, so... why would just calling fnDraw() reset iDisplayStart to 0? Is there a way I can force it to stay on the current page?
Thanks for any help. Here is my full table code, in case you see anything in there that might explain the problem (the fnServerData is there so that some custom filters from a form are taken into account whenever the table is redrawn):
[code]
var oTable = $('#messagesAntenne').dataTable( {
"bJQueryUI": true,
"bStateSave": true,
"iDisplayLength": 10,
"bLengthChange": false,
"sPaginationType": "full_numbers",
"bProcessing": true,
"bFilter": false,
"bLengthChange" : true,
"bServerSide" : true,
"bAutoWidth": false,
"sAjaxSource" : "index.php?option=com_cfmmessages&task=listmsg&format=json",
"fnServerData" : function (sSource, aoData, fnCallback) {
aoData.push({'name' : 'salon', 'value' : $('#salon').val()});
aoData.push({'name' : 'user', 'value' : $('#userFilter').val()});
aoData.push({'name' : 'dateFrom', 'value' : $('#dateFrom').val()});
aoData.push({'name' : 'dateTo', 'value' : $('#dateTo').val()});
$('input[name=statusFilter[]]').each(function(i) {
aoData.push({'name' : $(this).val(), 'value' : $(this).is(':checked')});
});
$.getJSON( sSource, aoData, function (json) {
fnCallback(json)
} );
}
} );
[/code]
This discussion has been closed.
Replies
You can't logically update a table that may not show the update. It is a huge UI mistake too. What page do new messages show up on? Are you always sorting by date?
You might consider a panel near the table that shows when new messages have been received but not loaded then the user would manually reload the table which should reset it to a state that shows the new messages. Reloading something and having a possibility of not showing the new information is just a bad User Experience.
I would take your requirements back to your Product Management or I totally missed your point.
I thought so too, honestly. They aren't always sorted by date, though it's the default sort (newer messags on top). But I did think it was pretty silly. I'll have to talk to them when I get the chance (they're in France and I'm in Canada so it's not that simple), but in the meantime, there's really no way to do that, then?
Also, this doesn't address the first issue I have. If I do refresh through the browser, it seems to "half" remember it's on page 2. Page 2 is pre-selected, but the data displayed is from page 1, and it says "Showing 11 to 20 of total 12 entries" which makes no sense...
I have discussed with our product manager and we decided to change the behaviour, so point 2) is moot now.
But I still have the problem described in 1). Can anyone help?
To re-iterate: If I select page 2, and refresh through the browser, page 2 is indeed pre-selected, but instead of displaying "Showing 11 to 12 of total 12 entries" and displaying the last 2 rows, it says, "Showing 11 to 20 of total 12 entries" (not very logical) and it shows the first 10 results from page 1.
I'll take another look to see what the error is (could be something like the Ajax call asking for page 1 and then the StateSave cookie changing the paginator to page 2 ?)
If I use the value stored in the cookie it works fine for the first load of the page (i.e reloading the data that corresponds to the previous page view) but on each subsequent table paginate event (e.g. clicking a page number button) it is always using the page number from the previous event.
Here's some code snippets:
[code]
$(document).ready( function() {
tblManualCRReferrals = $("#ManualCRRReferrals").dataTable( {
...
"fnServerData": getServerData
});
});
function getServerData( sSource, aoData, fnCallback ) {
var oData;
var sData = this.oApi._fnReadCookie("SpryMedia_DataTables_" + this.sInstance);
if (sData !== null && sData !== '') {
/* Try/catch the JSON eval - if it is bad then we ignore it */
try {
/* Use the JSON library for safety - if it is available */
if (typeof JSON == 'object' && typeof JSON.parse == 'function') {
/* DT 1.4.0 used single quotes for a string - JSON.parse doesn't allow this and throws an error. */
oData = JSON.parse(sData.replace(/'/g, '"'));
}
else {
oData = eval('(' + sData + ')');
}
}
catch (e) {
return;
}
for (var i =0; i < aoData.length; i++) {
if (aoData[i].name == "iDisplayStart") {
aoData[i].value = oData.iStart;
break;
}
}
}
// tell DataTables to get on with it
$.getJSON( sSource, aoData, function (json) {
if (json.sError != undefined) {
alert(json.sError);
}
fnCallback(json)
});
}
[/code]
So next step is to differentiate between loading the whole page (use values from the cookie) and a data refresh (use values from the pagination control).
[code]
$.fn.dataTableExt.oApi.fnFixPaginationParams = function(oSettings, aoData) {
if (oSettings.newPage == undefined) { // is this the first time a page load has been requested
var oData;
var sData = this.oApi._fnReadCookie("SpryMedia_DataTables_" + oSettings.sInstance);
if (sData !== null && sData !== '') {
// Try/catch the JSON eval - if it is bad then we ignore it
try {
// Use the JSON library for safety - if it is available
if (typeof JSON == 'object' && typeof JSON.parse == 'function') {
// DT 1.4.0 used single quotes for a string - JSON.parse doesn't allow this and throws
// an error. So for now we can do this. This can be removed in future it is just to
// allow the transfer to 1.4.1+ to occur
oData = JSON.parse(sData.replace(/'/g, '"'));
}
else {
oData = eval('(' + sData + ')');
}
for (var i = 0; i < aoData.length; i++) {
if (aoData[i].name == "iDisplayStart") {
aoData[i].value = oData.iStart;
break;
}
}
}
catch (e) {
// do nothing
}
oSettings.newPage = true;
}
}
}
var tblManualCRRReferrals;
$(document).ready( function() {
tblManualCRReferrals = $("#ManualCRRReferrals").dataTable( {
...
"fnServerData": getServerData
});
});
function getServerData(sSource, aoData, fnCallback ) {
if (tblManualCRRReferrals == undefined) {
return;
}
tblManualCRRReferrals.fnFixPaginationParams(aoData);
// tell DataTables to get on with it
$.getJSON( sSource, aoData, function (json) {
fnCallback(json)
});
}
// load the data into the table
jQuery(document).ready(function() {
tblManualCRRReferrals.fnDraw();
});
[/code]
See also my thread "Extensions not available when loading"
http://datatables.net/forums/comments.php?DiscussionID=2015&page=1#Item_1
[code]
$.fn.dataTableExt.oApi.fnFixPaginationParams = function(oSettings, aoData) {
if (oSettings.newPage == undefined) { // is this the first time a page load has been requested
var oData;
var sData = this.oApi._fnReadCookie("SpryMedia_DataTables_" + oSettings.sInstance);
if (sData !== null && sData !== '') {
// Try/catch the JSON eval - if it is bad then we ignore it
try {
// Use the JSON library for safety - if it is available
if (typeof JSON == 'object' && typeof JSON.parse == 'function') {
// DT 1.4.0 used single quotes for a string - JSON.parse doesn't allow this and throws
// an error. So for now we can do this. This can be removed in future it is just to
// allow the transfer to 1.4.1+ to occur
oData = JSON.parse(sData.replace(/'/g, '"'));
}
else {
oData = eval('(' + sData + ')');
}
for (var i = 0; i < aoData.length; i++) {
if (aoData[i].name == "iDisplayStart") {
aoData[i].value = oData.iStart;
break;
}
}
}
catch (e) {
// do nothing
}
oSettings.newPage = true;
}
}
}
var tblManualCRRReferrals;
$(document).ready( function() {
tblManualCRReferrals = $("#ManualCRRReferrals").dataTable( {
...
"fnServerData": getServerData
});
});
function getServerData(sSource, aoData, fnCallback ) {
// "this" now refers to the global var
this.fnFixPaginationParams(aoData);
// tell DataTables to get on with it
$.getJSON( sSource, aoData, function (json) {
fnCallback(json)
});
}
[/code]
Many thanks to Allan for the update.