fnFilterAll plugin question
fnFilterAll plugin question
Hi, I have a page with several datatable objects. Each datatable has the same set of columns but the rows in each table are for a specific status. Each table has the CSS class 'display'. There are a number of AJAX methods that can affect the data in one or more tables. When an action is taken that could affect the data in a table, the table source is replaced via an AJAX call and the dataTable() method called (with bDestroy: true) in the AJAX callback.
This much is working fine. Each of the datatable objects has its own search texbox (global -- not column-specific), but we want to have all grids filtered by a single master textbox. I tried using the fnFilterAll plugin and it works when the page is freshly loaded, but it breaks the first time search text is input after any of the tables is replaced by an AJAX action. I'll try to post the relevant code:
[code]
// global search "fnFilterAll() plugin by Kristoffer Karlstrom
$.fn.dataTableExt.oApi.fnFilterAll = function(oSettings, sInput, iColumn, bRegex, bSmart) {
if ( typeof bRegex == 'undefined' ) {
bRegex = false;
}
if ( typeof bSmart == 'undefined' ) {
bSmart = true;
}
for (var i in this.dataTableSettings) {
jQuery.fn.dataTableExt.iApiIndex = i;
this.fnFilter(sInput, iColumn, bRegex, bSmart);
}
jQuery.fn.dataTableExt.iApiIndex = 0;
}
$(document).ready(function(){
function initialize_grids(status_id) {
var selector = $('table.display');
oTable = selector.dataTable({
'aaSorting': [[0,'asc']],
'bDestroy' : true,
'sDom': '<"dataTables_header"lf<"clear">>rt<"dataTables_footer"ip<"clear">>',
'sPaginationType': 'full_numbers',
'aoColumnDefs': [
{ 'bSortable': false, 'aTargets': [ 2,3,6,8,9,10 ] }
]
});
// global-searchbox is an input element
$("#global-searchbox").keyup( function () {
/* Filter on the column (the index) of this element */
oTable.fnFilterAll(this.value);
} );
}
}
// example AJAX call that affects data in one of the tables
function add_is_mine_click_handlers(status_id) {
if (!status_id)
{
var selector = $(".is-mine");
} else {
var div_id = "status-table-" + status_id;
var selector = $("#" + div_id).find(".is-mine");
}
selector.click(
function() {
var thisCheck = $(this);
var project_id = thisCheck.attr('data-project-id');
var is_mine = thisCheck.is(":checked") ? 1 : 0;
var status_id = thisCheck.attr('data-status-id');
var div_id = "status-table-" + status_id;
$.post(MINE_CHANGE_URL, { project_id: project_id, is_mine: is_mine }, function(data) {
$("#"+div_id).html(data);
initialize_grids()
add_all_handlers(status_id);
});
return false;
})
}
function add_all_handlers(status_id) {
add_is_mine_click_handlers(status_id);
// calls to add other handlers here...
}
initialize_grids()
add_all_handlers();
});
[/code]
Thanks for any help!
[/code]
This much is working fine. Each of the datatable objects has its own search texbox (global -- not column-specific), but we want to have all grids filtered by a single master textbox. I tried using the fnFilterAll plugin and it works when the page is freshly loaded, but it breaks the first time search text is input after any of the tables is replaced by an AJAX action. I'll try to post the relevant code:
[code]
// global search "fnFilterAll() plugin by Kristoffer Karlstrom
$.fn.dataTableExt.oApi.fnFilterAll = function(oSettings, sInput, iColumn, bRegex, bSmart) {
if ( typeof bRegex == 'undefined' ) {
bRegex = false;
}
if ( typeof bSmart == 'undefined' ) {
bSmart = true;
}
for (var i in this.dataTableSettings) {
jQuery.fn.dataTableExt.iApiIndex = i;
this.fnFilter(sInput, iColumn, bRegex, bSmart);
}
jQuery.fn.dataTableExt.iApiIndex = 0;
}
$(document).ready(function(){
function initialize_grids(status_id) {
var selector = $('table.display');
oTable = selector.dataTable({
'aaSorting': [[0,'asc']],
'bDestroy' : true,
'sDom': '<"dataTables_header"lf<"clear">>rt<"dataTables_footer"ip<"clear">>',
'sPaginationType': 'full_numbers',
'aoColumnDefs': [
{ 'bSortable': false, 'aTargets': [ 2,3,6,8,9,10 ] }
]
});
// global-searchbox is an input element
$("#global-searchbox").keyup( function () {
/* Filter on the column (the index) of this element */
oTable.fnFilterAll(this.value);
} );
}
}
// example AJAX call that affects data in one of the tables
function add_is_mine_click_handlers(status_id) {
if (!status_id)
{
var selector = $(".is-mine");
} else {
var div_id = "status-table-" + status_id;
var selector = $("#" + div_id).find(".is-mine");
}
selector.click(
function() {
var thisCheck = $(this);
var project_id = thisCheck.attr('data-project-id');
var is_mine = thisCheck.is(":checked") ? 1 : 0;
var status_id = thisCheck.attr('data-status-id');
var div_id = "status-table-" + status_id;
$.post(MINE_CHANGE_URL, { project_id: project_id, is_mine: is_mine }, function(data) {
$("#"+div_id).html(data);
initialize_grids()
add_all_handlers(status_id);
});
return false;
})
}
function add_all_handlers(status_id) {
add_is_mine_click_handlers(status_id);
// calls to add other handlers here...
}
initialize_grids()
add_all_handlers();
});
[/code]
Thanks for any help!
[/code]
This discussion has been closed.
Replies
[code]
$('global-searchbox').unbind();
[/code]
to the very top of function initialize_grids(). All working fine now.
if ( !oSettings.oFeaturs.bFilter )
Allan
Certainly worth giving fnDestroy a go, before you replace the table, I'd say :-)
Allan
I got it working this way -- it's probably not the most efficient, but seems stable...
Basically, whenever I have to replace or remove a table via ajax, I do the following sequence:
1. Destroy all the grids
2. Replace the table content for whichever tables need to have their content changed.
3. Attach all the various event handlers to elements inside the table(s) whose content changed.
4. Initialize all the grids
5. Call fnFilterAll with whatever text happens to be in the global searchbox
6. Attach the keyup event to the global searchbox
It's important to add the various event handlers to the elements inside the tables before re-applying the filter -- if rows are hidden by the filter, the events won't get attached.
[code]
$(document).ready(function(){
function destroy_all_grids() {
$('#global-searchbox').unbind();
$('table.display').each(function() {
$(this).dataTable().fnDestroy();
})
}
function initialize_all_grids() {
var selector = $('table.display');
var searchbox = $('#global-searchbox');
var curSearch = searchbox.val();
selector.dataTable({
'aaSorting': [[0,'asc']],
'sDom': '<"dataTables_header"lf<"clear">>rt<"dataTables_footer"ip<"clear">>',
'sPaginationType': 'full_numbers'
});
selector.dataTable().fnFilterAll(curSearch);
// set the handler for key up on the searchbox
$("#global-searchbox").keyup( function () {
// Filter on the column (the index) of this element
selector.dataTable().fnFilterAll(this.value);
} );
}
function add_is_mine_click_handlers(status_id) {
if (!status_id)
{
var selector = $(".is-mine");
} else {
var div_id = "status-table-" + status_id;
var selector = $("#" + div_id).find(".is-mine");
}
selector.click(
function() {
var thisCheck = $(this);
var gParent = thisCheck.parent().parent();
var project_id = gParent.attr('data-project-id');
var is_mine = thisCheck.is(":checked") ? 1 : 0;
var status_id = gParent.attr('data-status-id');
var div_id = "status-table-" + status_id;
$.post(MINE_CHANGE_URL, { project_id: project_id, is_mine: is_mine }, function(data) {
var sel = $("#"+div_id);
destroy_all_grids();
sel.html(data);
add_all_handlers(status_id);
initialize_all_grids()
});
return false;
})
}
function add_all_handlers(status_id) {
add_is_mine_click_handlers(status_id);
// calls to add other handlers here...
}
add_all_handlers();
initialize_all_grids()
});
[/code]
Here's a better approach that seems to work reliably:
1. Unbind the global searchbox (remove ALL its handlers)
2. Destroy just the 5th grid datatable object
2. Replace the table content for the 5th grid
3. Attach all the various event handlers to elements inside the 5th table.
4. Initialize the grid on the 5th table
5. Call fnFilterAll on ALL grids with whatever text happens to be in the global searchbox
6. Attach the keyup event to the global searchbox for ALL grids
[code]
$(document).ready(function(){
function unbind_searchbox() {
$('#global-searchbox').unbind();
}
function rebind_searchbox_and_apply_current_filter() {
var searchbox = $('#global-searchbox');
var curSearch = searchbox.val();
searchbox.keyup( function () {
// Filter on the column (the index) of this element
$('table.display').dataTable().fnFilterAll(this.value);
} );
$('table.display').dataTable().fnFilterAll(curSearch);
}
function destroy_grids(status_id) {
if (!status_id) {
var selector = $('table.display');
} else {
var div_id = "status-table-" + status_id;
var selector = $("#" + div_id).find('table.display');
}
selector.each(function() {
$(this).dataTable().fnDestroy();
})
}
function initialize_grids(status_id) {
if (!status_id) {
var selector = $('table.display');
} else {
var div_id = "status-table-" + status_id;
var selector = $("#" + div_id).find('table.display');
}
selector.dataTable({
'aaSorting': [[0,'asc']],
'sDom': '<"dataTables_header"lf<"clear">>rt<"dataTables_footer"ip<"clear">>',
'sPaginationType': 'full_numbers',
'aoColumnDefs': [
{ 'bSortable': false, 'aTargets': [ 2,3,6,8,9,10 ] }
]
});
}
function add_is_mine_click_handlers(status_id) {
if (!status_id)
{
var selector = $(".is-mine");
} else {
var div_id = "status-table-" + status_id;
var selector = $("#" + div_id).find(".is-mine");
}
selector.click(
function() {
var thisCheck = $(this);
var gParent = thisCheck.parent().parent();
var project_id = gParent.attr('data-project-id');
var is_mine = thisCheck.is(":checked") ? 1 : 0;
var status_id = gParent.attr('data-status-id');
var div_id = "status-table-" + status_id;
$.post(MINE_CHANGE_URL, { project_id: project_id, is_mine: is_mine }, function(data) {
var sel = $("#"+div_id);
unbind_searchbox();
destroy_grids(status_id);
sel.html(data);
add_all_handlers(status_id);
});
return false;
})
}
function add_all_handlers(status_id) {
add_is_mine_click_handlers(status_id);
// calls to add other handlers here...
initialize_grids(status_id);
rebind_searchbox_and_apply_current_filter()
}
[/code]
Allan