Grouping Of Data Table On Button Click And Page Load

If you want to do a grouping of rows in data table on button click then you have to use this CDN.

Download and use it on your page.

I have resolved the bug, online cdn works for only first load of data table not for the click event of table and re rendering of data table.

Code

/*! RowsGroup for DataTables v2.0.0

/**

 * 
 * License      MIT - http://datatables.net/license/mit
 *
 * This feature plug-in for DataTables automatically merges columns cells
 * based on it's values equality. It supports multi-column row grouping
 * in according to the requested order with dependency from each previous 
 * requested columns. Now it supports ordering and searching. 
 * Please see the example.html for details.
 * 
 * Rows grouping in DataTables can be enabled by using any one of the following
 * options:
 *
 * * Setting the `rowsGroup` parameter in the DataTables initialisation
 *   to array which containes columns selectors
 *   (https://datatables.net/reference/type/column-selector) used for grouping. i.e.
 *    rowsGroup = [1, 'columnName:name', ]
 * * Setting the `rowsGroup` parameter in the DataTables defaults
 *   (thus causing all tables to have this feature) - i.e.
 *   `$.fn.dataTable.defaults.RowsGroup = [0]`.
 * * Creating a new instance: `new $.fn.dataTable.RowsGroup( table, columnsForGrouping );`
 *   where `table` is a DataTable's API instance and `columnsForGrouping` is the array
 *   described above.
 *
 * For more detailed information please see:
 *     
 */
(function($) {
    ShowedDataSelectorModifier = {
        order: 'current',
        page: 'current',
        search: 'applied',
    }
    GroupedColumnsOrderDir = 'asc';
    /*
     * columnsForGrouping: array of DTAPI:cell-selector for columns for which rows grouping is applied
     */
    var RowsGroup = function(dt, columnsForGrouping) {
        this.first = true;
        this.table = dt.table();
        this.columnsForGrouping = columnsForGrouping;
        // set to True when new reorder is applied by RowsGroup to prevent order() looping
        this.orderOverrideNow = false;
        this.mergeCellsNeeded = false; // merge after init
        this.order = []
        var self = this;
        dt.on('order.dt', function(e, settings) {
            if (!self.orderOverrideNow) {
                self.orderOverrideNow = true;
                if (self.first) {
                    self._updateOrderAndDrawFirst();
                    self.first = false;
                } else {
                    self._updateOrderAndDraw()
                }
            } else {
                self.orderOverrideNow = false;
            }
        })
        dt.on('preDraw.dt', function(e, settings) {
            if (self.mergeCellsNeeded) {
                self.mergeCellsNeeded = false;
                self._mergeCells()
            }
        })
        dt.on('column-visibility.dt', function(e, settings) {
            self.mergeCellsNeeded = true;
        })
        dt.on('search.dt', function(e, settings) {
            // This might to increase the time to redraw while searching on tables
            //   with huge shown columns
            self.mergeCellsNeeded = true;
        })
        dt.on('page.dt', function(e, settings) {
            self.mergeCellsNeeded = true;
        })
        dt.on('length.dt', function(e, settings) {
            self.mergeCellsNeeded = true;
        })
        dt.on('xhr.dt', function(e, settings) {
            self.mergeCellsNeeded = true;
        })
        this._updateOrderAndDrawFirst();
        /* Events sequence while Add row (also through Editor)
         * addRow() function
         *   draw() function
         *     preDraw() event
         *       mergeCells() - point 1
         *     Appended new row breaks visible elements because the mergeCells() on previous step doesn't apllied to already processing data
         *   order() event
         *     _updateOrderAndDraw()
         *       preDraw() event
         *         mergeCells()
         *       Appended new row now has properly visibility as on current level it has already applied changes from first mergeCells() call (point 1)
         *   draw() event
         */
    };
    RowsGroup.prototype = {
        setMergeCells: function() {
            this.mergeCellsNeeded = true;
        },
        mergeCells: function() {
            this.setMergeCells();
            this.table.draw();
        },
        _getOrderWithGroupColumns: function(order, groupedColumnsOrderDir) {
            if (groupedColumnsOrderDir === undefined) groupedColumnsOrderDir = GroupedColumnsOrderDir
            var self = this;
            var groupedColumnsIndexes = this.columnsForGrouping.map(function(columnSelector) {
                return self.table.column(columnSelector).index()
            })
            var groupedColumnsKnownOrder = order.filter(function(columnOrder) {
                return groupedColumnsIndexes.indexOf(columnOrder[0]) >= 0
            })
            var nongroupedColumnsOrder = order.filter(function(columnOrder) {
                return groupedColumnsIndexes.indexOf(columnOrder[0]) < 0
            })
            var groupedColumnsKnownOrderIndexes = groupedColumnsKnownOrder.map(function(columnOrder) {
                return columnOrder[0]
            })
            var groupedColumnsOrder = groupedColumnsIndexes.map(function(iColumn) {
                var iInOrderIndexes = groupedColumnsKnownOrderIndexes.indexOf(iColumn)
                if (iInOrderIndexes >= 0) return [iColumn, groupedColumnsKnownOrder[iInOrderIndexes][1]]
                else return [iColumn, groupedColumnsOrderDir]
            })
            groupedColumnsOrder.push.apply(groupedColumnsOrder, nongroupedColumnsOrder)
            return groupedColumnsOrder;
        },
        // Workaround: the DT reset ordering to 'asc' from multi-ordering if user order on one column (without shift)
        //   but because we always has multi-ordering due to grouped rows this happens every time
        _getInjectedMonoSelectWorkaround: function(order) {
            if (order.length === 1) {
                // got mono order - workaround here
                var orderingColumn = order[0][0]
                var previousOrder = this.order.map(function(val) {
                    return val[0]
                })
                var iColumn = previousOrder.indexOf(orderingColumn);
                if (iColumn >= 0) {
                    // assume change the direction, because we already has that in previos order
                    return [
                        [orderingColumn, this._toogleDirection(this.order[iColumn][1])]
                    ]
                } // else This is the new ordering column. Proceed as is.
            } // else got milti order - work normal
            return order;
        },
        _mergeCells: function() {
            var columnsIndexes = this.table.columns(this.columnsForGrouping, ShowedDataSelectorModifier).indexes().toArray()
            var showedRowsCount = this.table.rows(ShowedDataSelectorModifier)[0].length
            this._mergeColumn(0, showedRowsCount - 1, columnsIndexes)
        },
        // the index is relative to the showed data
        //    (selector-modifier = {order: 'current', page: 'current', search: 'applied'}) index
        _mergeColumn: function(iStartRow, iFinishRow, columnsIndexes) {
            var columnsIndexesCopy = columnsIndexes.slice()
            currentColumn = columnsIndexesCopy.shift()
            currentColumn = this.table.column(currentColumn, ShowedDataSelectorModifier)
            var columnNodes = currentColumn.nodes()
            var columnValues = currentColumn.data()
            var newSequenceRow = iStartRow,
                iRow;
            for (iRow = iStartRow + 1; iRow <= iFinishRow; ++iRow) {
                if (columnValues[iRow] === columnValues[newSequenceRow]) {
                    $(columnNodes[iRow]).hide()
                } else {
                    $(columnNodes[newSequenceRow]).show()
                    $(columnNodes[newSequenceRow]).attr('rowspan', (iRow - 1) - newSequenceRow + 1)
                    if (columnsIndexesCopy.length > 0) this._mergeColumn(newSequenceRow, (iRow - 1), columnsIndexesCopy)
                    newSequenceRow = iRow;
                }
            }
            $(columnNodes[newSequenceRow]).show()
            $(columnNodes[newSequenceRow]).attr('rowspan', (iRow - 1) - newSequenceRow + 1)
            if (columnsIndexesCopy.length > 0) this._mergeColumn(newSequenceRow, (iRow - 1), columnsIndexesCopy)
        },
        _toogleDirection: function(dir) {
            return dir == 'asc' ? 'desc' : 'asc';
        },
        _updateOrderAndDraw: function() {
            this.mergeCellsNeeded = true;
            var currentOrder = this.table.order();
            currentOrder = this._getInjectedMonoSelectWorkaround(currentOrder);
            this.order = this._getOrderWithGroupColumns(currentOrder)
            this.table.order($.extend(true, Array(), this.order))
            this.table.draw()
        },
        _updateOrderAndDrawFirst: function() {
            this.mergeCellsNeeded = true;
            // var currentOrder = this.table.order();
            // currentOrder = this._getInjectedMonoSelectWorkaround(currentOrder);
            // this.order = this._getOrderWithGroupColumns(currentOrder)
            // this.table.order($.extend(true, Array(), this.order))
            this.table.draw()
        },
    };
    $.fn.dataTable.RowsGroup = RowsGroup;
    $.fn.DataTable.RowsGroup = RowsGroup;
    // Automatic initialisation listener
    $(document).on('init.dt', function(e, settings) {
        if (e.namespace !== 'dt') {
            return;
        }
        var api = new $.fn.dataTable.Api(settings);
        if (settings.oInit.rowsGroup || $.fn.dataTable.defaults.rowsGroup) {
            options = settings.oInit.rowsGroup ? settings.oInit.rowsGroup : $.fn.dataTable.defaults.rowsGroup;
            var rowsGroup = new RowsGroup(api, options);
            $.fn.dataTable.Api.register('rowsgroup.update()', function() {
                rowsGroup.mergeCells();
                return this;
            });
            $.fn.dataTable.Api.register('rowsgroup.updateNextDraw()', function() {
                rowsGroup.setMergeCells();
                return this;
            });
        }
    });
    $(document).on('destroy.dt', function(e, settings) {
        var api = new $.fn.dataTable.Api(settings);
        api.off('order.dt');
        api.off('preDraw.dt');
        api.off('column-visibility.dt');
        api.off('search.dt');
        api.off('page.dt');
        api.off('length.dt');
        api.off('xhr.dt');
    });
}(jQuery));
/*

TODO: Provide function which determines the all <tr>s and <td>s with "rowspan" html-attribute is parent (groupped) for the specified <tr> or <td>. To use in selections, editing or hover styles.

TODO: Feature
Use saved order direction for grouped columns
    Split the columns into grouped and ungrouped.

    user = grouped+ungrouped
    grouped = grouped
    saved = grouped+ungrouped

    For grouped uses following order: user -> saved (because 'saved' include 'grouped' after first initialisation). This should be done with saving order like for 'groupedColumns'
    For ungrouped: uses only 'user' input ordering
*/