Any better approaches in achieving multi-column grouping in UI-Grid header?
Asked Answered
H

2

6

I've tried to achieve the multi column grouping at column header level for UI-Grid with the below approach.

Steps I've followed:

  1. Include the below header cell template, of the UI grid, with another "UI Grid row":

    <div class="ui-grid-header custom-ui-grid-header">
        <div class="ui-grid-top-panel">
            <div class="ui-grid-header-viewport">
                <div class="ui-grid-header-canvas">
                    <div class="ui-grid-header-cell-wrapper" ng-style="colContainer.headerCellWrapperStyle()">
                        <div class="ui-grid-header-cell-row">
                            <div class="ui-grid-header-cell" ng-repeat="superCol in grid.options.superColDefs track by $index" col-name="{{superCol.name}}">
                                <div class="ui-grid-cell-contents" data-ng-bind="superCol.displayName">
                                </div>
                            </div>
                        </div>
                        <div class="ui-grid-header-cell-row">
                            <div class="ui-grid-header-cell ui-grid-clearfix" ng-repeat="col in colContainer.renderedColumns track by col.colDef.name" ui-grid-header-cell super-col-width-update col="col" render-index="$index">
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div ui-grid-menu></div>
        </div>
    </div>
    
  2. Add references to your column definition object of the grid with Super Column data:

    $scope.gridOptions = {
        headerTemplate: 'views/header-template.html',
        superColDefs: [{
            name: 'group1',
            displayName: 'Group 1'
        }, {
            name: 'group2',
            displayName: 'Group 2'
        }],
        columnDefs: [{
            name: 'name',
            displayName: 'Name',
            superCol: 'group1'
        }, {
            name: 'title',
            displayName: 'Title',
            superCol: 'group1'
        }, {
            name: 'age',
            displayName: 'Age',
            superCol: 'group2'
        }],
        data: [{
            name: 'Bob',
            title: 'CEO',
            age: '31'
        }, {
            name: 'Frank',
            title: 'Lowly Developer',
            age: '33'
        }]
    };
    
  3. Calculate the width of each header column, and add it to its Super Column's width, to make each relevant Super cell to span across its child cells. This can be achieved with a directive that will be placed on every child header cell.

Directive:

.directive('superColWidthUpdate', ['$timeout', function ($timeout) {
    return {
        'restrict': 'A',
            'link': function (scope, element) {
            var _colId = scope.col.colDef.superCol,
                _el = jQuery(element);
            _el.on('resize', function () {
                _updateSuperColWidth();
            });
            var _updateSuperColWidth = function () {
                $timeout(function () {
                    var _parentCol = jQuery('.ui-grid-header-cell[col-name="' + _colId + '"]');
                    var _parentWidth = _parentCol.outerWidth(),
                        _width = _el.outerWidth();
                    _parentWidth = ((_parentWidth === 1) ? 0 : _parentWidth) + _width + 'px';
                    _parentCol.css({
                        'min-width': _parentWidth,
                        'max-width': _parentWidth
                    });
                }, 0);
            };
            _updateSuperColWidth();
        }
    };
}]);

In the above approach, the solution has been achieved by manipulating the DOM to render a new header row and new header cells, which sit on top of the usual header row, but within the Header viewport.

However, I am looking for any better approach, may be by using the UI-Grid's internal services or directives. Any help here is much appreciated!

Helenehelenka answered 10/4, 2015 at 14:41 Comment(0)
P
0

There is a much simpler solution. Write a customTreeAggregationFn that just calculates the identity of the row you wish to include in the grouping row:

var customTreeAggregationFn = function(aggregation, fieldValue, value, row) {
    // calculates the average of the squares of the values
    if (typeof(aggregation.count) === 'undefined') {
        aggregation.count = 0;
    }
    aggregation.count++;
    aggregation.value = value;
}

Then use this function and filter out non-header rows in the cellTemplate:

{
        name: 'EDM Id',
        displayName: 'EDM Id',
        field: 'Entity__r.EDM_ID__c',
        enableColumnMenu: false,
        customTreeAggregationFinalizerFn: function(aggregation) {
            aggregation.rendered = aggregation.value;
        },
        cellTemplate: '<div><div class="ui-grid-cell-contents" ng-if="row.groupHeader" title="TOOLTIP">{{COL_FIELD CUSTOM_FILTERS}}</div></div>',
        customTreeAggregationFn: customTreeAggregationFn

    }

enter image description here

Portmanteau answered 3/12, 2015 at 21:56 Comment(0)
E
2

Multi-column grouping isn't something the grid currently does, one of the barriers is the interaction with column moving and resizing.

Having said that, we'd like to have it, and looking at what you've done I reckon you could help to write that feature in ui-grid.

I'm not entirely sure how column grouping would work with column moving - perhaps it would always force the entire group to move, not just an individual column. With resizing basically the group column needs to have a width that equals the total of the sub columns.

That's basically what you've done already, the only trick is to fit it into the feature structure.

If you want to drop onto the gitter channel sometime https://gitter.im/angular-ui/ng-grid you'd probably find people would give you a hand with that PR (probably @c0bra would be best placed to help)

Essene answered 10/4, 2015 at 20:57 Comment(1)
Thanks @PaulL. I will be glad to join the gitter channel and be part, however, I am trying to make my solution more optimal for my scenario, and I will be joining once I have a complete working solution with me.Helenehelenka
P
0

There is a much simpler solution. Write a customTreeAggregationFn that just calculates the identity of the row you wish to include in the grouping row:

var customTreeAggregationFn = function(aggregation, fieldValue, value, row) {
    // calculates the average of the squares of the values
    if (typeof(aggregation.count) === 'undefined') {
        aggregation.count = 0;
    }
    aggregation.count++;
    aggregation.value = value;
}

Then use this function and filter out non-header rows in the cellTemplate:

{
        name: 'EDM Id',
        displayName: 'EDM Id',
        field: 'Entity__r.EDM_ID__c',
        enableColumnMenu: false,
        customTreeAggregationFinalizerFn: function(aggregation) {
            aggregation.rendered = aggregation.value;
        },
        cellTemplate: '<div><div class="ui-grid-cell-contents" ng-if="row.groupHeader" title="TOOLTIP">{{COL_FIELD CUSTOM_FILTERS}}</div></div>',
        customTreeAggregationFn: customTreeAggregationFn

    }

enter image description here

Portmanteau answered 3/12, 2015 at 21:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.