Skip to content

Commit

Permalink
New: -init stateLoadCallback can now use a callback function for as…
Browse files Browse the repository at this point in the history
…ync loading of state data, rather than requiring it to be sync.

- async is deprecated in the XHR spec and will be removed from Chrome
  (m53 I think).
  • Loading branch information
Allan Jardine committed Aug 22, 2016
1 parent f821fe6 commit 6f2fe93
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 234 deletions.
73 changes: 49 additions & 24 deletions docs/option/stateLoadCallback.xml
Expand Up @@ -5,42 +5,67 @@
<since>1.10</since>

<type type="function">
<signature>stateLoadCallback( settings )</signature>
<signature>stateLoadCallback( settings, callback )</signature>
<parameter type="DataTables.Settings" name="settings">
DataTables settings object
</parameter>
<returns type="object">
Data retrieved from storage containing the saved state (from `dt-init stateSaveCallback`)
<parameter type="function" name="callback" since="1.10.13">
Callback function that should be executed when the state data is ready if loaded by Ajax or some other asynchronous method. If this option is to be used the `stateLoadCallback` method must return `-type undefined` (i.e. just don't return anything)!
</parameter>
<returns type="object|undefined">
If the data is loaded synchronously the return value should be the loaded state (or `null` if no data was loaded).

If the data will be loaded asynchronously (e.g. via Ajax), `-type undefined` should be returned (just don't use a return statement!) and the callback function called when the state has been loaded. Please note that this option required DataTables 1.10.13 or newer.
</returns>
<scope>HTML table element</scope>
</type>

<description>
With this callback you can define where, and how, the state of a table is loaded from. By default DataTables will load from `localStorage` but you might wish to use a server-side database or cookies as your implementation requirements demand. For the format of the data that is stored, please refer to the `dt-init stateSaveCallback` documentation.
With this callback you can define where, and how, the state of a table is loaded from. By default DataTables will load from [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) or [`sessionStrorage`](https://developer.mozilla.org/en/docs/Web/API/Window/sessionStorage), but for more permanent storage, you can store state in a server-side database.

Prior to DataTables 1.10.13 this method had to act synchronously, i.e. the state would be returned by the function. As of 1.10.13 it is possible to load state asynchronously via Ajax or any other async method and execute the callback function, passing in the loaded state.

To maintain backwards compatibility the state can still be returned synchronously. To use the callback method, simply don't return a value from your `-init stateLoadCallback` function. See below for examples of both use cases.

Note that this callback works hand-in-hand with `dt-init stateSaveCallback`. This callback loads the state from storage when the table is reloaded while `dt-init stateSaveCallback` saves it.
</description>

<example title="Load state from a server via Sjax"><![CDATA[
$('#example').dataTable( {
"stateSave": true,
"stateLoadCallback": function (settings) {
var o;
// Send an Ajax request to the server to get the data. Note that
// this is a synchronous request since the data is expected back from the
// function
$.ajax( {
"url": "/state_load",
"async": false,
"dataType": "json",
"success": function (json) {
o = json;
}
} );
return o;
}
<example title="Load state from a server via Ajax (1.10.13 or newer)"><![CDATA[
$('#example').DataTable( {
stateSave: true,
stateLoadCallback: function (settings, callback) {
$.ajax( {
url: '/state_load',
async: false,
dataType: 'json',
success: function (json) {
callback( json );
}
} );
}
} );
]]></example>

<example title="Load state from a server via Sjax (prior to 1.10.13)"><![CDATA[
$('#example').DataTable( {
stateSave: true,
stateLoadCallback: function (settings) {
var o;
// Send an Ajax request to the server to get the data. Note that
// this is a synchronous request since the data is expected back from the
// function
$.ajax( {
url: '/state_load',
async: false,
dataType: 'json',
success: function (json) {
o = json;
}
} );
return o;
}
} );
]]></example>

Expand Down
196 changes: 95 additions & 101 deletions js/core/core.constructor.js
Expand Up @@ -337,128 +337,122 @@ if ( rowOne.length ) {
}

var features = oSettings.oFeatures;
var loadedInit = function () {
/*
* Sorting
* @todo For modularisation (1.11) this needs to do into a sort start up handler
*/

/* Must be done after everything which can be overridden by the state saving! */
if ( oInit.bStateSave )
{
features.bStateSave = true;
_fnLoadState( oSettings, oInit );
_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
}
// If aaSorting is not defined, then we use the first indicator in asSorting
// in case that has been altered, so the default sort reflects that option
if ( oInit.aaSorting === undefined ) {
var sorting = oSettings.aaSorting;
for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
}
}

/* Do a first pass on the sorting classes (allows any size changes to be taken into
* account, and also will apply sorting disabled classes if disabled
*/
_fnSortingClasses( oSettings );

/*
* Sorting
* @todo For modularisation (1.11) this needs to do into a sort start up handler
*/
if ( features.bSort ) {
_fnCallbackReg( oSettings, 'aoDrawCallback', function () {
if ( oSettings.bSorted ) {
var aSort = _fnSortFlatten( oSettings );
var sortedColumns = {};

// If aaSorting is not defined, then we use the first indicator in asSorting
// in case that has been altered, so the default sort reflects that option
if ( oInit.aaSorting === undefined )
{
var sorting = oSettings.aaSorting;
for ( i=0, iLen=sorting.length ; i<iLen ; i++ )
{
sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
}
}
$.each( aSort, function (i, val) {
sortedColumns[ val.src ] = val.dir;
} );

/* Do a first pass on the sorting classes (allows any size changes to be taken into
* account, and also will apply sorting disabled classes if disabled
*/
_fnSortingClasses( oSettings );
_fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
_fnSortAria( oSettings );
}
} );
}

if ( features.bSort )
{
_fnCallbackReg( oSettings, 'aoDrawCallback', function () {
if ( oSettings.bSorted ) {
var aSort = _fnSortFlatten( oSettings );
var sortedColumns = {};
if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
_fnSortingClasses( oSettings );
}
}, 'sc' );

$.each( aSort, function (i, val) {
sortedColumns[ val.src ] = val.dir;
} );

_fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
_fnSortAria( oSettings );
}
/*
* Final init
* Cache the header, body and footer as required, creating them if needed
*/

// Work around for Webkit bug 83867 - store the caption-side before removing from doc
var captions = $this.children('caption').each( function () {
this._captionSide = $this.css('caption-side');
} );
}

_fnCallbackReg( oSettings, 'aoDrawCallback', function () {
if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
_fnSortingClasses( oSettings );
var thead = $this.children('thead');
if ( thead.length === 0 ) {
thead = $('<thead/>').appendTo($this);
}
}, 'sc' );
oSettings.nTHead = thead[0];

var tbody = $this.children('tbody');
if ( tbody.length === 0 ) {
tbody = $('<tbody/>').appendTo($this);
}
oSettings.nTBody = tbody[0];

/*
* Final init
* Cache the header, body and footer as required, creating them if needed
*/
var tfoot = $this.children('tfoot');
if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
// If we are a scrolling table, and no footer has been given, then we need to create
// a tfoot element for the caption element to be appended to
tfoot = $('<tfoot/>').appendTo($this);
}

// Work around for Webkit bug 83867 - store the caption-side before removing from doc
var captions = $this.children('caption').each( function () {
this._captionSide = $this.css('caption-side');
} );
if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
$this.addClass( oClasses.sNoFooter );
}
else if ( tfoot.length > 0 ) {
oSettings.nTFoot = tfoot[0];
_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
}

var thead = $this.children('thead');
if ( thead.length === 0 )
{
thead = $('<thead/>').appendTo(this);
}
oSettings.nTHead = thead[0];
/* Check if there is data passing into the constructor */
if ( oInit.aaData ) {
for ( i=0 ; i<oInit.aaData.length ; i++ ) {
_fnAddData( oSettings, oInit.aaData[ i ] );
}
}
else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
/* Grab the data from the page - only do this when deferred loading or no Ajax
* source since there is no point in reading the DOM data if we are then going
* to replace it with Ajax data
*/
_fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
}

var tbody = $this.children('tbody');
if ( tbody.length === 0 )
{
tbody = $('<tbody/>').appendTo(this);
}
oSettings.nTBody = tbody[0];
/* Copy the data index array */
oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();

var tfoot = $this.children('tfoot');
if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
{
// If we are a scrolling table, and no footer has been given, then we need to create
// a tfoot element for the caption element to be appended to
tfoot = $('<tfoot/>').appendTo(this);
}
/* Initialisation complete - table can be drawn */
oSettings.bInitialised = true;

if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
$this.addClass( oClasses.sNoFooter );
}
else if ( tfoot.length > 0 ) {
oSettings.nTFoot = tfoot[0];
_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
}
/* Check if we need to initialise the table (it might not have been handed off to the
* language processor)
*/
if ( bInitHandedOff === false ) {
_fnInitialise( oSettings );
}
};

/* Check if there is data passing into the constructor */
if ( oInit.aaData )
/* Must be done after everything which can be overridden by the state saving! */
if ( oInit.bStateSave )
{
for ( i=0 ; i<oInit.aaData.length ; i++ )
{
_fnAddData( oSettings, oInit.aaData[ i ] );
}
features.bStateSave = true;
_fnLoadState( oSettings, oInit, loadedInit );
_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
}
else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' )
{
/* Grab the data from the page - only do this when deferred loading or no Ajax
* source since there is no point in reading the DOM data if we are then going
* to replace it with Ajax data
*/
_fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
else {
loadedInit();
}

/* Copy the data index array */
oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();

/* Initialisation complete - table can be drawn */
oSettings.bInitialised = true;

/* Check if we need to initialise the table (it might not have been handed off to the
* language processor)
*/
if ( bInitHandedOff === false )
{
_fnInitialise( oSettings );
}

0 comments on commit 6f2fe93

Please sign in to comment.