bStateSave and pagination

bStateSave and pagination

MorriganMorrigan Posts: 10Questions: 0Answers: 0
edited April 2010 in General
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]

Replies

  • TomCTomC Posts: 43Questions: 0Answers: 0
    Your features are mutually exclusive.

    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.
  • MorriganMorrigan Posts: 10Questions: 0Answers: 0
    edited April 2010
    Hi Tom,

    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...
  • MorriganMorrigan Posts: 10Questions: 0Answers: 0
    Hello,

    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.
  • fastbikefastbike Posts: 14Questions: 0Answers: 0
    I'm getting the same problem. If page 2 is selected in the table, when we return to the page hosting teh table, the pagination control shows page 2 but the data returned from the server is 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 ?)
  • fastbikefastbike Posts: 14Questions: 0Answers: 0
    I've looked at updating the value of the iDisplayStart paramter before fetching the data from the Ajax URL.
    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).
  • fastbikefastbike Posts: 14Questions: 0Answers: 0
    edited May 2010
    I've fixed this problem with an extension method. Here's my final code snippets.

    [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
  • fastbikefastbike Posts: 14Questions: 0Answers: 0
    With the changes in v1.7 (currently in beta) this code gets even simpler, no longer having to work around the uninitialised global datatable var.

    [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.
This discussion has been closed.