I use saveState: true
to keep the conditions for my DataTables when ordering columns or filtering results with a phrase. When I refresh the page, conditions remain thanks to the local storage.
This works great within the same browser, but imagine a quite common case when after you apply both ordering and filtering to your table, you would like to share this state with your friend by simply copying and pasting a long URL with query string so that when he / she opens it, the table looks exactly the same.
There was a plugin that used to do this but it's not maintained and after giving it a try, it does not work with 1.10
at all.
I wonder if it's possible to solve this by using just some native code and maybe replaceState
to simply update URL when applicable?
This is my JS.
$(function () {
let table = $('.table').DataTable({
processing: true,
serverSide: true,
stateSave: true,
stateSaveCallback: function (settings, data) {
// save state
},
stateLoadCallback: function (settings) {
// read state and change url?
},
ajax: '{{ route('admin.api.dt.customer.index') }}',
columnDefs: [{
orderable: false,
targets: 2
}],
pageLength: {{ \App\Repositories\Sync\CustomerRepository::PER_PAGE_DATA_TABLES }},
});
});
Based on this code the obvious issue is that the ajax
URL is my hardcoded Laravel route. Simply passing query string does not work.
My Laravel route is simply: http://iosportal.local/admin/api/customer
and it returns JSON
shaped to suit DataTables.
Can somebody give my a hint how to approach this?
I found storing the current state as base64 was easy and portable. You'll need to create some sort of UI for retrieving the state which could be anything, but at it's most primitive you can have a button:
<a id="get-state">Get State</a>
And do something like this:
$('#get-state').on('click', function() {
var saved = btoa(JSON.stringify(table.state()));
alert('state=' + saved);
})
Now append that as a query parameter to get your shareable url:
https://example.com?state=eyJzZWFyY2giOnsic2VhcmNoIjoibWVvdyIsInNtYXJ0Ijp0cnVlLCJyZWdleCI6ZmFsc2UsImNhc2VJbnNlbnNpdGl2ZSI6dHJ1ZX19
To restore the state have something like this in your datatable options:
// use stateLoadParams() instead of stateLoadCallback() since the former
// is designed for exactly what you want — manipulating the loaded state
"stateLoadParams": function (settings, data) {
// check the current url to see if we've got a state to restore
var url = new URL(window.location.href);
var state = url.searchParams.get("state");
if (state) {
// if so, try to base64 decode it and parse into object from a json
try {
state = JSON.parse(atob(state));
// now iterate over the object properties and assign any that
// exist to the current loaded state (skipping "time")
for (var k in state) {
if (state.hasOwnProperty(k) && k != 'time') {
data[k] = state[k];
}
}
} catch (e) {
console.error(e);
}
}
}
Note I'm skipping the "time" attribute here since it may invalidate your state expiry.
@billynoah's stateLoadParams
utilization should work for most of the scenarios, however stateLoadParams
is being fired only when there is already some saved state in local storage - so when you are accessing given URL for the first time it will not work.
Much cleaner way how to achieve sharing through URL is to edit stateSaveCallback and stateLoadCallback so they will not use local storage at all and will use url parameters instead.
This is all you need to add into respective datatable config. It also updates URL without need of reload on every state change (or state save to be more precise) and it works for multiple tables as well. The only requirement is that every table element has to have its own specific ID.
stateSaveCallback: function (settings, data) {
//encode current state to base64
const state = btoa(JSON.stringify(data));
//get query part of the url
let searchParams = new URLSearchParams(window.location.search);
//add encoded state into query part
searchParams.set($(this).attr('id') + '_state', state);
//form url with new query parameter
const newRelativePathQuery = window.location.pathname + '?' + searchParams.toString() + window.location.hash;
//push new url into history object, this will change the current url without need of reload
history.pushState(null, '', newRelativePathQuery);
},
stateLoadCallback: function (settings) {
const url = new URL(window.location.href);
let state = url.searchParams.get($(this).attr('id') + '_state');
//check the current url to see if we've got a state to restore
if (!state) {
return null;
}
//if we got the state, decode it and add current timestamp
state = JSON.parse(atob(state));
state['time'] = Date.now();
return state;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With