Bugfix for table initialization
Bugfix for table initialization
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
The problem is that ajax is an async function. You are using
table2API.ajax.reload();
right after initializing the Datatable. Your code: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
What you are suggesting would require
ajax.reload()
to be altered to block untilinitComplete
is fired, or would require additional blocking code everywhereajax.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 whereinit
andinitComplete
are fired) on success. However, the__reload()
function (called byajax.reload()
) callsabort()
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()
soinit
andinitComplete
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).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
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 inajax.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 ininitComplete
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 .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