fnFilterAll plugin question

fnFilterAll plugin question

ddrakeddrake Posts: 16Questions: 0Answers: 0
edited January 2012 in Plug-ins
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]

Replies

  • ddrakeddrake Posts: 16Questions: 0Answers: 0
    edited January 2012
    Ok, I think I have it. I just added a line:
    [code]
    $('global-searchbox').unbind();
    [/code]
    to the very top of function initialize_grids(). All working fine now.
  • ddrakeddrake Posts: 16Questions: 0Answers: 0
    Nope - not fine. If I do an AJAX action that replaces a table, then try another search, the search appears to work (i.e. all tables are filtered appropriately, but I get "oSettings is null" in line 1520:
    if ( !oSettings.oFeaturs.bFilter )
  • allanallan Posts: 63,683Questions: 1Answers: 10,498 Site admin
    When you replace the old table with your new one form the Ajax request, are you destroying the old table ( fnDestroy )? If not, then the reference in the settings object is still there, to a table that is no longer int he DOM, hence the error I think.

    Allan
  • ddrakeddrake Posts: 16Questions: 0Answers: 0
    Thanks, Allan! I'll try that. I thought that was going to be taken care of by setting 'bDestroy' : true in the constructor -- I didn't know about fnDestroy...
  • allanallan Posts: 63,683Questions: 1Answers: 10,498 Site admin
    Generally speaking, yes, bDestroy and fnDestroy are the same thing - however, I think the difference in this case is that you've already nuked the HTML for the old table on the page when the destroy function is called.

    Certainly worth giving fnDestroy a go, before you replace the table, I'd say :-)

    Allan
  • ddrakeddrake Posts: 16Questions: 0Answers: 0
    edited January 2012
    Thanks very much, 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]
  • ddrakeddrake Posts: 16Questions: 0Answers: 0
    Ok, so this approach does work in the sense that nothing breaks. But if we're displaying 5 tables stacked vertically and we do some AJAX action that forces us to replace the 5th (bottom) table, it appears to the user as if the whole page has reloaded, because we're tearing down and rebuilding all the grids.

    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]
  • allanallan Posts: 63,683Questions: 1Answers: 10,498 Site admin
    Nice one - thanks for posting your solution :-)

    Allan
This discussion has been closed.