Browser functionality with ajax-driven datatables
Browser functionality with ajax-driven datatables
pinetree.rg
Posts: 6Questions: 0Answers: 0
Hi
I've managed to get the browser functions to work with ajax-driven datatables. A quick search of the forums didn't show something similar so I thought I'd post how I did it. I used a jquery plugin called hashchange (http://benalman.com/projects/jquery-hashchange-plugin/). It is based on tracking the url anchor hash, so what I did was make the hash change on every draw of the table.
As you can see the table is called from a custom function and not on $(document).ready, since you need to provide data to initialize the datatable.
Here's the code:
[code]
var oTable; //var to store the table init data
var checkHash = 0; //a control variable used to check if the table is drawing for the first time
function getTable(pg, sortCol, sortDir){ //function to get the table, params: page, sorting column, sorting direction
checkHash = 0; //set this var to 0 so fnDrawCallback doesn't execute on first draw
$.get(
"the_table.php", //any script to get your table goes here, this one just outputs 100 rows of numbers
function(data){
$("#mainDiv").html(data); //get the table on the page
oTable = $("#table").dataTable({ //initialize datatable
"sPaginationType": "full_numbers",
"iDisplayStart": pg, //get it from the function parameter
"iDisplayLength": 10, // a fixed num of pages makes it easier to maintain the hash,
"bLengthChange": false, // otherwise, you would have to reflect that in the hash also
"aaSorting": [[sortCol, sortDir]], //get these from the function parameters
"fnDrawCallback": function(){ //this is where you change the hash
if ( checkHash != 0 ){ //if this is the first draw event don't change the hash
var settings = oTable.fnSettings();
var sorting = settings.aaSorting[0];
var sortedColumn = sorting[0];
var sortedDirection = sorting[1];
var page = settings._iDisplayStart;
window.location.hash = 'table-' + page +'-'+ sortedColumn +'-'+ sortedDirection;
//it's important to have a single separator of variables in the hash
}
}
});
checkHash = 1; //set checkHash to 1, meaning that the table has been drawn at least once
}
);
}
$(document).ready( //this is where the parsing of the hash happens, triggers on refresh, back, forward, and similar
function(){
var path = $(location).attr('hash'); //get the hash
if (path==''){
getTable(0, 1, 'asc'); //if there is no hash, get the table with desired initial options
//in this case, start on page 1, sort by second column in ascending order
}
else{
pathArr = path.split('-'); //split the hash into array using '-' as spliting character
getTable(parseInt(pathArr[1]), parseInt(pathArr[2]), pathArr[3]);
//call the getTable with options given in the hash
}
$(window).hashchange( function(){
//this is the same set of instructions as above, only included into a hashchange event
var path = $(location).attr('hash');
if (path==''){ getTable(0, 1, 'asc');}
else{
pathArr = path.split('-');
getTable(parseInt(pathArr[1]), parseInt(pathArr[2]), pathArr[3]);
}
});
}
);
[/code]
You need to stop the fnDataCallback from executing on the first draw, because if you don't then the hashchange event goes into a loop which causes the table to go back one, forward one page endlessly.
Also if you have many ajax-driven tables which you initialize into the same oTable var the fnDrawCallback would use the data from the previous table.
And if you get to the table from a link or another function, you can setup the first hash update there, setting a href anchor, or using the window.location.hash method.
You also need to have the hash parsing code outside the hashchange event, in order for bookmarking and refreshing to work (where the hash isn't changing).
Feel free to ask any questions, but as I'm a complete javascript/jquery/other programming noob, I apologize in advance if i can't help you.
PS. If this is something really trivial, and everyone has already come up with their own solution, I apologize, but feel free to post your own solutions.
I've managed to get the browser functions to work with ajax-driven datatables. A quick search of the forums didn't show something similar so I thought I'd post how I did it. I used a jquery plugin called hashchange (http://benalman.com/projects/jquery-hashchange-plugin/). It is based on tracking the url anchor hash, so what I did was make the hash change on every draw of the table.
As you can see the table is called from a custom function and not on $(document).ready, since you need to provide data to initialize the datatable.
Here's the code:
[code]
var oTable; //var to store the table init data
var checkHash = 0; //a control variable used to check if the table is drawing for the first time
function getTable(pg, sortCol, sortDir){ //function to get the table, params: page, sorting column, sorting direction
checkHash = 0; //set this var to 0 so fnDrawCallback doesn't execute on first draw
$.get(
"the_table.php", //any script to get your table goes here, this one just outputs 100 rows of numbers
function(data){
$("#mainDiv").html(data); //get the table on the page
oTable = $("#table").dataTable({ //initialize datatable
"sPaginationType": "full_numbers",
"iDisplayStart": pg, //get it from the function parameter
"iDisplayLength": 10, // a fixed num of pages makes it easier to maintain the hash,
"bLengthChange": false, // otherwise, you would have to reflect that in the hash also
"aaSorting": [[sortCol, sortDir]], //get these from the function parameters
"fnDrawCallback": function(){ //this is where you change the hash
if ( checkHash != 0 ){ //if this is the first draw event don't change the hash
var settings = oTable.fnSettings();
var sorting = settings.aaSorting[0];
var sortedColumn = sorting[0];
var sortedDirection = sorting[1];
var page = settings._iDisplayStart;
window.location.hash = 'table-' + page +'-'+ sortedColumn +'-'+ sortedDirection;
//it's important to have a single separator of variables in the hash
}
}
});
checkHash = 1; //set checkHash to 1, meaning that the table has been drawn at least once
}
);
}
$(document).ready( //this is where the parsing of the hash happens, triggers on refresh, back, forward, and similar
function(){
var path = $(location).attr('hash'); //get the hash
if (path==''){
getTable(0, 1, 'asc'); //if there is no hash, get the table with desired initial options
//in this case, start on page 1, sort by second column in ascending order
}
else{
pathArr = path.split('-'); //split the hash into array using '-' as spliting character
getTable(parseInt(pathArr[1]), parseInt(pathArr[2]), pathArr[3]);
//call the getTable with options given in the hash
}
$(window).hashchange( function(){
//this is the same set of instructions as above, only included into a hashchange event
var path = $(location).attr('hash');
if (path==''){ getTable(0, 1, 'asc');}
else{
pathArr = path.split('-');
getTable(parseInt(pathArr[1]), parseInt(pathArr[2]), pathArr[3]);
}
});
}
);
[/code]
You need to stop the fnDataCallback from executing on the first draw, because if you don't then the hashchange event goes into a loop which causes the table to go back one, forward one page endlessly.
Also if you have many ajax-driven tables which you initialize into the same oTable var the fnDrawCallback would use the data from the previous table.
And if you get to the table from a link or another function, you can setup the first hash update there, setting a href anchor, or using the window.location.hash method.
You also need to have the hash parsing code outside the hashchange event, in order for bookmarking and refreshing to work (where the hash isn't changing).
Feel free to ask any questions, but as I'm a complete javascript/jquery/other programming noob, I apologize in advance if i can't help you.
PS. If this is something really trivial, and everyone has already come up with their own solution, I apologize, but feel free to post your own solutions.
This discussion has been closed.
Replies
That's superb - thanks for posting that!
I guess there can be come kind of security on the server-side ass well, in order to prevent someone modifying the hash and reading damaging data from your server ( "..-..-etc-passwd" springs to mind - although obviously apache itself will catch that one!).
Regards,
Allan
So if you mess with the hash, you only get a table with no datatables initialized over it, so you get a big unpaginated unsortable table.
I guess you could check if the hash is the proper format when parsing it and either show an alert or initialize with default settings.
I'm sure this will come in handy - there have been one or two requests in the past which look for something like this. I'm sure I'll be linking to your post in future!
Regards,
Allan
It seems that the hashchange event seems to cause the table to reload on every change of the hash.
So if you go to next page, or re-sort the table, the hash changes, the plugin picks it up, and instead of just datatables doing their work to go to the next page, the plugin actually calls the getTable() function with the new settings in the hash, which kinda defeats the purpose of datatables (which in this case is not going to the server for the table on each sort or page change).
So with ajax-based pages that have no datatables, the plugin does what it's supposed to, but here it does it on account of datatables functionality.
If anyone has an idea how to fix this, please do respond.
Well, i have an idea, but i don't know how to execute it.
The idea would be to trigger the $(window).hashchange only if the change in the hash came from using the back or forward button. If there's a way to do that, then this would work as intended i think.
Or, (this seems better) instead of calling the getTable() function again, simply redrawing the table with the parameters from hash.
Edit:
So i decided to go with a redraw of the datatable, rather than a recall of the function. So i wrote this little api extension (modeled it after fnStandingRedraw)to use instead of getTable() calls in the hashchange event function:
[code]
$.fn.dataTableExt.oApi.fnCustomRedraw = function(oSettings, page, sortCol, sortDir) {
oSettings._iDisplayStart = page;
oSettings.aaSorting[0][0] = sortCol;
oSettings.aaSorting[0][1] = sortDir;
oSettings.oApi._fnCalculateEnd(oSettings);
oSettings.oApi._fnDraw(oSettings);
/*if((sortCol != 0) && (sortDir !=0)){
oTable.fnSort( [ [sortCol, sortDir] ] );
}*/
};
[/code]
However, the sorting thing isn't working, and when i do the sort at the end(the commented lines) it causes a weird loop in the hashchange. So i'd like to setup sorting options before i redraw the table, but i haven't found an example of how to do that, if possible.
While this works without going to the server for every change, it seems that using the datatables functions are doing double work.
If you re-sort or go to another page, datatable first draws the page, then calls the fnDrawCallback, which sets the hash with new options, then the hashchange event picks up the change, and then actually redraws the page with the same settings.
I guess I'm kinda lost on this one...
However, I'd still like to know if there's a way of setting the sorting options before redrawing in the above function.
There is a small bug where you have to click twice for every even sort you do on the same column (first time sorts as expected, second time you have to click twice, third once, fourth twice and so on). If I figure this out, I'll post it here, of course.
The fnCustomRedraw still causes the table to be drawn twice, as explained in the previous post, but it does it fast enough it's unnoticable, at least with the table size i'm working with (around 100 rows).
Here's the new code:
[code]
$.fn.dataTableExt.oApi.fnCustomRedraw = function(oSettings, page, sortCol, sortDir) {
checkHash = 0;
if((sortCol != 0) && (sortDir !=0)){
oTable.fnSort( [ [sortCol, sortDir] ] );
}
oSettings._iDisplayStart = page;
oSettings.oApi._fnCalculateEnd(oSettings);
oSettings.oApi._fnDraw(oSettings);
checkHask = 1;
};
[/code]
Oh, and there is the checkHash variable set to 0, and at the and back to 1, since you don't want to trigger the DrawCallback which changes the hash when using the browser buttons.
This is from another project I'm working on, not the same as the first post, so once I include this into that test example, I will post the whole code.