Bugfix for table initialization

Bugfix for table initialization

awelchawelch Posts: 38Questions: 1Answers: 3

I have come across an issue with the initialization features when using ajax sourced data. Namely, if ajax.reload() is called before the first ajax call returns init will never fire and initComplete will never be called. You can find a demonstration of the bug here. As you can see, the top table has the initComplete function fired successfully, the bottom table does not. There are many cases, in the solution I work with, where actions on a page will reload a DataTable (calling ajax.reload()) and can potentially be fired before the table finishes its first ajax call. This will block any code in the initComplete function. I have a working solution which modifies the _fnBuildAjax() function. My solution consists of modifying the "error" property of the baseAjax variable to include a call to _fnInitComplete() if it hasn't been called yet:

/**
* Create an Ajax call based on the table's settings, taking into account that
* parameters can have multiple forms, and backwards compatibility.
*
* @param {object} oSettings dataTables settings object
* @param {array} data Data to send to the server, required by
*     DataTables - may be augmented by developer callbacks
* @param {function} fn Callback function to run when data is obtained
*/
function _fnBuildAjax( oSettings, data, fn )
{
…
var baseAjax = {
    "data": data,
    "success": function (json) {
        var error = json.error || json.sError;
        if ( error ) {
            _fnLog( oSettings, 0, error );
        }
    
        oSettings.json = json;
        callback( json );
    },
    "dataType": "json",
    "cache": false,
    "type": oSettings.sServerMethod,
    "error": function (xhr, error, thrown) {
        var ret = _fnCallbackFire( oSettings, null, "xhr", [oSettings, null, oSettings.jqXHR] );
    
        if ( $.inArray( true, ret ) === -1 ) {
            if ( error == "parsererror" ) {
                _fnLog( oSettings, 0, "Invalid JSON response", 1 );
            }
            else if ( xhr.readyState === 4 ) {
                _fnLog( oSettings, 0, "Ajax error", 7 );
            }
        }

        _fnProcessingDisplay(oSettings, false);

        if ( !oSettings._bInitComplete ) {
            _fnInitComplete(oSettings);
        }   
    }
};
…
}

Replies

  • kthorngrenkthorngren Posts: 21,322Questions: 26Answers: 4,948

    The problem is that ajax is an async function. You are using table2API.ajax.reload(); right after initializing the Datatable. Your code:

      var table1API = $table1.DataTable(settings);
      var table2API = $table2.DataTable(settings);
      table2API.ajax.reload();
    

    The reload is executed synchronously before the ajax response. This isn't a Datatables issue but the way ajax behaves. This allows the page to continue working while waiting for the ajax response.

    Not sure I understand what you are trying to do but one option may be to set a flag in initComplete that allows ajax.reload() to work. If the flag is not set then your code shouldn't execute ajax.reload(). Basically you need to find a way to know that the Datatable has completely loaded before you allow ajax.reload() to execute.

    Kevin

  • awelchawelch Posts: 38Questions: 1Answers: 3

    What you are suggesting would require ajax.reload() to be altered to block until initComplete is fired, or would require additional blocking code everywhere ajax.reload() is used. This is one way to handle this problem but blocking code waiting on an asynchronous call is not a good solution as it will interfere with UX. The root of this problem is that the _fnInitialise() function sets up the initial ajax request to call _fnInitComplete() (this is where init and initComplete are fired) on success. However, the __reload() function (called by ajax.reload()) calls abort() on the pending jqHXR object. This means that the success function of the ajax call does not get called, rather, the error function will get called. Subsequent ajax calls will not include the call to _fnInitComplete() so init and initComplete will never be fired. The solution I've proposed (and am using currently) alleviates this problem with no impact on the UX and a negligible impact on performance (just an extra if statement that only gets executed when an ajax error occurs).

  • allanallan Posts: 63,489Questions: 1Answers: 10,470 Site admin
    edited April 2019

    That's really interesting - thanks for posting this. I'd never really though about doing an async initialisation and immediately doing an async data load.

    What's the use case for that? It sounds like there is a redundant Ajax call since the data from the first one will effectively never be used.

    Allan

  • awelchawelch Posts: 38Questions: 1Answers: 3

    In my case, most pages will have interactive elements that will reload a DataTable on said page (calling ajax.reload()). Most of the interactive elements will change a value in ajax.data before resubmitting to the server. As an example, a view into a CMS where a user has a toggle between viewing all the content or just the content that belongs to them. Between network travel time and DB interaction time there is a small window where a user can access the button while the table is still waiting on its first ajax request to return, this is where the problem arises. An impatient user clicks to change the view before the ajax request returns, the table reloads with different data than the original call but none of the hookups in initComplete happen so the table is crippled and the whole page must be reloaded. The example I provided was very (maybe overly) simplified just to demonstrate what is happening. Also, I found the demo ajax calls for live.datatable.net are a bit too quick to manually click a button before the first load.

    As @kthrongren suggested, I could block any subsequent ajax requests until the first load but this would leave the user thinking the page is unresponsive for a time (not to mention having to override ajax.reload() or adding additional code everywhere it's called). Worse yet, the user may think their interaction worked and they are looking at a view of the table they are not actually looking at.

    I suppose I could just take all the code out of initComplete and run the hookups synchronously after instantiating the table but that would make me sad :'( .

  • allanallan Posts: 63,489Questions: 1Answers: 10,470 Site admin

    I'm with you now. And I agree that this is something that should be fix. I can't promise when I'll get to it (mega backlog at the moment), but it is in our bug system now.

    Allan

This discussion has been closed.