Delayed Cell Value Loading without Server-side Processing
Delayed Cell Value Loading without Server-side Processing
While we have a full server-side solution on the road map and being applied where the biggest tables are, at the moment, I'm working on improving the client-side datatables where the data size is moderate (e.g. 20-100 rows) by adding pagination and I'm feeling a little painted into a corner.
The original client-side code is a bit cheeky- we have a column that is expensive to compute, so we render the non-expensive cells then make an ajax call to get the expensive data and update the row element node directly e.g.
[code]
- cheap cell data-
$(function() {
ajaxRequest(null, 'path/to/data', {
data: {
params: params
},
beforeSend: function() {
var target = $(document.getElementById('{tr-identifer}').cells[1]);
loadingIndicator(target, "Loading...");
},
success: function(data) {
var row = document.getElementById('{tr-identifer}');
var expensiveData = $(data)[0];
row.cells[1].innerHTML = expensiveData.innerHTML;
}
})
})
[/code]
First observation is we should be using something like fnUpdate to make sure we're not breaking the table rather than hacking in the innerHtml.
Second problem comes when introducing pagination- we don't want to make these ajax calls until a row is displayed. The fnDrawCallback seemed like the right callback to be using- I assume it's called whenever we've redrawn due to moving to a new page- however the oSettings object doesn't seem documented at all and I'm not sure I can ascertain from it which rows have been drawn in order to update them (and then wouldn't the fnUpdate trigger another redraw, so I need to latch the ajax call to only occur once?).
Is fnDrawCallback the right call back or should I be using another callback instead? Is oSettings documented anywhere? Is what I'm doing with this code just a bit crazy and should I throw in the towel and just insist on going full server-side?
The original client-side code is a bit cheeky- we have a column that is expensive to compute, so we render the non-expensive cells then make an ajax call to get the expensive data and update the row element node directly e.g.
[code]
- cheap cell data-
$(function() {
ajaxRequest(null, 'path/to/data', {
data: {
params: params
},
beforeSend: function() {
var target = $(document.getElementById('{tr-identifer}').cells[1]);
loadingIndicator(target, "Loading...");
},
success: function(data) {
var row = document.getElementById('{tr-identifer}');
var expensiveData = $(data)[0];
row.cells[1].innerHTML = expensiveData.innerHTML;
}
})
})
[/code]
First observation is we should be using something like fnUpdate to make sure we're not breaking the table rather than hacking in the innerHtml.
Second problem comes when introducing pagination- we don't want to make these ajax calls until a row is displayed. The fnDrawCallback seemed like the right callback to be using- I assume it's called whenever we've redrawn due to moving to a new page- however the oSettings object doesn't seem documented at all and I'm not sure I can ascertain from it which rows have been drawn in order to update them (and then wouldn't the fnUpdate trigger another redraw, so I need to latch the ajax call to only occur once?).
Is fnDrawCallback the right call back or should I be using another callback instead? Is oSettings documented anywhere? Is what I'm doing with this code just a bit crazy and should I throw in the towel and just insist on going full server-side?
This discussion has been closed.
Replies
You are absolutely correct - fnUpdate would need to be used - however, be careful since by default it will cause a redraw, which might muck up your fnDrawCallback :-)
So yes, fnDrawCallback sounds like the right thing to do to me. Get a list of all of the rows that are currently on display (a unique identifier for each, such as the primary key perhaps?) and then submit a single request to the server for all of those rows to be calculated. You could send individual requests, one for each row (queued otherwise you'd DoS your own server!) but I think it would probably be faster over all to just send one request - unless the calculations take a really significant amount of time each (seconds each, although that's my subjective opinion!).
One question - why do you think server-side processing will resolve this issue? Wouldn't you still have the same problem in that the calculation needs to be done, but it takes a long time to do - or is it that it would take a long time in the browser, but not the server?
Allan
With client-side processing, if we used embedded AJAX queries, even though the rows aren't visible (thanks to the datatables paginating on top of the data), all the AJAX queries would go off at once.
Should I instead use fnPreDrawCallback and cancel the draw if any updates need to be made, relying on fnUpdate call to queue up another draw after the update is completed? Would those fnUpdate calls have to be made after the draw 'fails' rather than inline with the function? Is there a post-draw callback that could trigger a queue of those calls and safely trigger a redraw inline?
There is an option on fnUpdate to not redraw, so I can disable that for all but the last update made in that queue at least.
Also, I still have no idea how to work out what the active/visible rows are when paginating client-side.
How does that work? JSON can't contain Javascript functions, so you'd need something monitoring the returned JSON I guess, that itself will create the Ajax query and fire it back at the server?
> Also, I still have no idea how to work out what the active/visible rows are when paginating client-side.
In 1.9- you can use the $() method: http://datatables.net/docs/DataTables/1.9.4/#$ .
In 1.10+ use the selector modifier options: http://next.datatables.net/reference/type/selector-modifier
Allan
[code]
$(function() {
doStuff();
});
[/code]
... you can trigger js functions when the AJAX call succeeds, but obviously jQuery is playing a part here.
I solved the active/visible rows by using fnRowCallback, which is only invoked when a row is ready to display. I invoke an AJAX call that uses the above mechanism to call a function which updates the table (obviously outside of the redraw process). All you need is a guard mechanism on the fnRowCallback to prevent infinite looping if the AJAX call fails or is in process.
Clearly fnRowCallback was the critical element to implementing this behavior, for the benefit of anyone who's reading this looking for a solution.
Allan