DataTables and display/collapse/expand of a tree view

DataTables and display/collapse/expand of a tree view

torrtorr Posts: 1Questions: 1Answers: 0

Hello DataTables developers,

so far forum states that displaying tree view like data in a DataTable is not possible, this is clearly not true, well, with some restrictions. The below source snippets are intended to be used with DataTables 1.10.12 or later.

It is possible to have DataTables display data that looks like a tree, given a server component that does the following:
- add an "id" attribute containing the row id attribute to every tr
- add a "rev" attribute containing the ids of the parent nodes of the row (separated by what you like, i used a dash) to every tr
- add a "data-depth" attribute to the first td of every tr with a depth > 0
- add properly formatting of every td with a data-depth attribute using CSS

First you need to add the following callbacks to your DataTables initialization options:

$(".dataTable").dataTable({
    ...<your other options>...
    fnStateLoadCallback: function(settings){
        // Code to load the table state from a data source of your choice,
        // i use a server side component that adds a data attribute for the state
        var state = this.data("state") || {};
        if (state.collapsed) settings.aCollapsed = state.collapsed;
        return state;
    },
    fnStateSaveParams: function(settings,state){
        if (settings.aCollapsed) state.collapsed = settings.collapsed;
    }
});

This callbacks will add/restore information about collapsed elements to/from the table state.

Removing children of collapsed parents is done by this simple custom filter::

$.fn.dataTable.ext.search.push(function(settings,filterData,dataIndex,data,searchCounter)
{
    if (!settings.aCollapsed) return true;
    var row = $(settings.aoData[dataIndex].nTr),
        rev = row.attr('rev'),
        notCollapsed = true;
    if (rev === undefined) return true;
    row.removeClass('hidden');
    rev = rev.split('-');
    for (var i = 0; i < rev.length; i++) {
        if (settings.aCollapsed.indexOf(parseInt(rev[i])) !== -1) {
            notCollapsed = false;
            break;
        }
    }
    return notCollapsed;
});

Collapsing single rows then is done by adding a click handler on each row which calls an API extension like:

$.extend($.fn.dataTable.ext.internal,{
    fnToggleCollapse: function(settings,id)
    {
        if (!settings.aCollapsed) return;
        id = parseInt(id);
        var row = $(this.dataTableSettings[0].aIds[id].nTr),
            index = settings.aCollapsed.indexOf(id);
        if (index === -1) settings.aCollapsed.push(id);
        else settings.aCollapsed.splice(index,1);
        row.toggleClass('collapsed');
        this._fnReDraw();
    },
}

However, i have been unable to encapsulate all the callbacks i needed for my implementation into an extension, since at the point the 'preInit.dt.dtk' event callback is run, the fnStateLoadCallback already has been called.

Searching/filtering with the above custom filter works well, except for one restriction: forcing display of rows excluded by the DataTables (global or per-row) keyword search was impossible due to API restrictions.

So if one of you DataTables developers could consider adding:
- an event which is run just before calling fnStateLoadCallback that can modify/set fnStateLoadCallback from a plugin, or that can be used instead of fnStateLoadCallback from a plugin
- an event which a plugin could hook instead of overriding fnStateSaveCallback
- an option to let process custom filters all rows instead of only previously filtered ones, which will make them able to include previously excluded rows in the result with result values like described below
to DataTables core this would be great.

For the latter option i suggest the following return values from custom filters:
- true: include record disregarding previous filtering
- false: exclude record
- null/undefined: previous filter decides

This wouldn't break the existing API, since an option would have to be activated first for this filtering behaviour.

With this new events and options it shouldn't be anymore required to override internal API methods such as _fnFilter() or _fnFilterComplete() and thus should render any previous request to override this internal API methods useless.

Just some thoughts for improvement of a great library,
torr

This discussion has been closed.