9

I'm using jQuery Datatables and I would like to incorporate row grouping into the table.

I have attempted to incorporate it myself by adding rows and a click event handler to expand/collapse the rows pertaining to that row group. This relies on toggling the visibility of the rows, which works but is messy.

I had an issue here with large tables where most rows aren't in the DOM until a scroll event calls drawCallback, so when I gave rows a certain class to associate them with a row group, the classes were removed on every scroll of the table.

Datatables recommends using their rowGroup extension which I have currently incorporated into my table. https://datatables.net/extensions/rowgroup/

This extension has no option to expand or collapse a group, does anyone have any experience manipulating this extension to add expand/collapse functionality?

I have attempted to override $.fn.dataTable.ext.search.push to simulate a "filter" that won't draw certain rows, which I can do. The issue here is that I can't decide which row is a rowGroup row in this method to draw so all of the rowGroup rows are removed.

Has anyone had any luck expanding/collapsing groups using the rowGroup extension?

Paul T.
  • 4,703
  • 11
  • 25
  • 29
justinlav
  • 155
  • 1
  • 3
  • 11

4 Answers4

23

First add a state to keep track of collapsed groups:

 var collapsedGroups = {};

Next, add this to the Datatable initialization to enable the rowGroup plugin. It works by checking collapapsedGroups and then this info to hide or show the rows. It also adds a css-class indicating if it's collapsed or not:

 {
    rowGroup: {
        // Uses the 'row group' plugin
        dataSrc: 'product.category',
        startRender: function (rows, group) {
            var collapsed = !!collapsedGroups[group];

            rows.nodes().each(function (r) {
                r.style.display = collapsed ? 'none' : '';
            });    

            // Add category name to the <tr>. NOTE: Hardcoded colspan
            return $('<tr/>')
                .append('<td colspan="8">' + group + ' (' + rows.count() + ')</td>')
                .attr('data-name', group)
                .toggleClass('collapsed', collapsed);
        }
  }

Finally, add a handler for clicking on the row for collapsing/expanding rows. This causes a redraw of the table which, in turn, calls startRender above:

   $tbody.on('click', 'tr.group-start', function () {
        var name = $(this).data('name');
        collapsedGroups[name] = !collapsedGroups[name];
        table.draw();
    });

Here is a working example.

Martin Wickman
  • 19,662
  • 12
  • 82
  • 106
  • 1
    Very nice! One slight change, add `false` to `table.draw()`, otherwise if you expand/collapse a group on the third page, for example, the `draw()` would return you to the first page. Here's an implementation of that for anyone wanting to see it in action: live.datatables.net/cecosoru/1/edit – colin0117 Jul 12 '18 at 07:20
  • 2
    Just a quick note that hard coding the colspan can be eliminated by referencing `rows.columns()[0].length`, such as: `.append('' + group + ' (' + rows.count() + ')')` – Loren Maxwell Apr 17 '19 at 12:51
  • I have been trying for hours to make this work!!, i saw colin live example, tried to replicate, I just cant, dont know why, but cant...> Any guide? – Yorki Bonilla Sep 30 '19 at 03:50
  • the collapsing only seems to work for the first and last level of rowgrouping. adding a third level and clicking the middle row does not result in hidden the rowgroup under it – Patrick Kwinten May 29 '20 at 11:53
  • Is there a way to have them collapsed on page load? I have been trying and trying to figure it out and have had no luck – BeerusDev Sep 04 '20 at 13:45
  • If it may helps someone, in `rowGroup`, I had to add the property `startClassName: 'group-start'` to make it work, as the `click` event of the live example is based on `tr.group-start` – AlexB Oct 19 '20 at 15:32
7

You can also add a toggler icon to indicate collapse state (using font awesome):

startRender: function (rows, group) {
    var collapsed = !!collapsedGroups[group];

    rows.nodes().each(function (r) {
        r.style.display = collapsed ? 'none' : '';
    });

    var toggleClass = collapsed ? 'fa-plus-square' : 'fa-minus-square';

    // Add group name to <tr>
    return $('<tr/>')
        .append('<td colspan="' + rows.columns()[0].length +'">' + '<span class="fa fa-fw ' + toggleClass + ' toggler"/> ' + group + ' (' + rows.count() + ')</td>')
        .attr('data-name', group)
        .toggleClass('collapsed', collapsed);
},
ZooZ
  • 933
  • 1
  • 17
  • 25
0

I have found my own answer so I want to share it if anyone else has future problems with this.

Use the below code to implement row Grouping where index is the column index that you want to group by:

var dataSrc = g_oTable.columns(index).dataSrc();

g_oTable.order([index, "asc"]).draw();
g_oTable.order.fixed({
    pre: [ index, 'asc' ]
}).draw();

g_oTable.rowGroup().dataSrc(dataSrc[0]);
g_oTable.rowGroup().enable().draw();

$('.group').each(function(i, obj) {
    $(obj).nextUntil('tr.group').each(function(i) {
        $(this).toggle();
    });
});
g_oTable.draw();

Then add a click event handler to your row Groups:

$( document ).on("click", "tr.group", function(){
    $(this).nextUntil('tr.group').toggle();
});
justinlav
  • 155
  • 1
  • 3
  • 11
  • Although this solution seems concise, it has some problems and should probably not be accepted. In particular, the behaviour is unexpected when the number of result entries shown is changed and the last displayed group is expanded and showing only a subset of the results. The above solution offered by Martin Wickman does not have this weakness and has merit. A benefit of using a css class for toggle is that it's very easy to set up styling to show the usual +/- for expand/collapse. The hard coded colspan is a small price, and with a bit of thought it can probably be eliminated. – flayman Jul 03 '18 at 15:10
0

My quick and dirty solution is this...

$('.group').click(function() {
    //$(this+" .group_date .date_arrow").addClass("rotateSVG");
    var nextItem = $(this).next('tr');
    while(nextItem.attr('class') != 'group') {
        nextItem.fadeToggle();
        if(nextItem.next('tr').length == 0) {
            break;
        }
        nextItem = nextItem.next('tr');
    }
});
MomasVII
  • 4,641
  • 5
  • 35
  • 52