二十八、UI-Grid Grouping 分组

原文:Tutorial: 209 Grouping

分组功能允许您根据特定列中的相似值对行进行分组, 在某些方面类似于 excel 数据透视表的效果。不按分组的列可以聚合, 例如, 统计每个组中的行数。

api 文档中提供有”分组” 的功能, 包括分组本身和 treeBase 文档中的共享函数。特别是:

grouping columnDef and treeBase columnDef
grouping gridOptions and treeBase gridOptions
grouping publicApi and treeBase publicApi
树结构本身被记录在 treeBase。

可以在 columnDef 选项中来设置分组:
grouping: { groupPriority: 0 }
或为列中的聚合设置 treeAggregation: {type: uiGridGroupingConstants.aggregation.COUNT}.

默认情况下分组列会显示在前面, 这将提供更直观的效果。为了避免对pinning 的依赖关系, 是将分组的列作为整体来移动的, 而不是使用pinning 功能。

分组有排序功能, 允许用户更改排序顺序或使用外部排序功能, 并将结果分组。标记为分组的列排序优先级最高。

任何分组的列都有 suppressRemoveSort 设置, 当列被取消分组时, suppressRemoveSort 将返回到 columnDef 中的值。

分组和聚合应与筛选一起使用, 它应只对筛选的行进行分组和聚合。

无法编辑组页眉行, 如果使用选择功能, 则无法选择。但是, 它们可以被导出。

默认情况下, 组 rowHeader 始终可见。如果希望 groupRowHeader 仅在至少一列分组时出现, 则可以在gridOption中设置 treeRowHeaderAlwaysVisible: false

表格初始化之后更改分组, 可以调用以下方法:

groupColumn:对单个列进行分组。将其添加到当前分组的末尾, 因此, 如果希望这是唯一的分组, 首先需要删除现有的分组列。添加一个asc排序 , 如果没有一个
ungroupColumn:取消单个列
aggregateColumn:设置列的聚合, 包括关闭聚合。首先自动删除任何排序。
setGrouping:将所有分组集中在一个序列中, 删除现有分组
getGrouping:获取表格的分组配置
clearGrouping:清除当前分组设置
以下几点需要注意:
– treeIndent:随着分组级别的加深, 展开按钮将缩进多个像素 (默认为 10)。更大的值看起来更好, 但占用更多的空间。
– treeRowHeaderWidth:分组行标题的宽度
– customTreeAggregationFinalizerFn:如果列有一个 cellFilter, 则插入文本 (例如 “min: xxxx”) 通常会破坏 cellFilter。可以定义一个自定义聚合终结器, 以不同的方式处理此文本, 可以在代码中应用筛选, 或者跳过包含的聚合文本。这也可以用来跳过计数的统计。

如果要取消分组列中的数据 (只显示在groupHeader 行中), 则可以通过对允许分组的任何列重写 cellTemplate, 如下所示:

cellTemplate: '<div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents" title="TOOLTIP">{{COL_FIELD CUSTOM_FILTERS}}</div>'

在下面的示例中, 此操作仅在状态列上完成。这不包括在基本代码中, 因为它可能与用户的自定义模板进行交互。

调整聚合工作的方式可以通过定义 columnsProcessor (比 groupingColumnProcessor 更高 (更高) 的优先级 (高于 400), 以及查找分组或聚合列并更改诸如 treeAggregationFn 或 customTreeAggregationFinalizerFn 的内容来完成。有关示例, 请参见教程320。

Example

在这个例子中, 先按 “状态” 列,然后是 “性别” 列, 统计名称 , 并找到每个分组的最大年龄, 最后计算平均余额。我们禁止在 “平衡” 列上显示聚合文本, 因为我们希望将其格式化为货币… 但这意味着我们不能轻易地看到它是一个平均值。

我们编写一个函数来提取状态和性别的聚合数据 (如果您更改了分组, 则此函数将停止工作), 并将它们写入控制台。

代码:
index.html

<!doctype html>
<html ng-app="app">
  <head>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-touch.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-animate.js"></script>
    <script src="http://ui-grid.info/docs/grunt-scripts/csv.js"></script>
    <script src="http://ui-grid.info/docs/grunt-scripts/pdfmake.js"></script>
    <script src="http://ui-grid.info/docs/grunt-scripts/vfs_fonts.js"></script>
    <script src="http://ui-grid.info/release/ui-grid.js"></script>
    <script src="http://ui-grid.info/release/ui-grid.css"></script>
    <script src="app.js"></script>
  </head>
  <body>
    <div ng-controller="MainCtrl">
      <button id="expandAll" type="button" class="btn btn-success" ng-click="expandAll()">Expand All</button>
      <button id="toggleFirstRow" type="button" class="btn btn-success" ng-click="toggleRow(0)">Toggle First Row</button>
      <button id="toggleSecondRow" type="button" class="btn btn-success" ng-click="toggleRow(1)">Toggle Second Row</button>
      <button id="changeGrouping" type="button" class="btn btn-success" ng-click="changeGrouping()">Change Grouping</button>
      <button id="getAggregates" type="button" class="btn btn-success" ng-click="getAggregates()">Get Aggregates</button>
      <div id="grid1" ui-grid="gridOptions" ui-grid-grouping class="grid"></div>
    </div>
  </body>
</html>

main.css

.grid {
  width: 500px;
  height: 400px;
}

app.js

var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.grouping' ]);

app.controller('MainCtrl', ['$scope', '$http', '$interval', 'uiGridGroupingConstants', function ($scope, $http, $interval, uiGridGroupingConstants ) {
  $scope.gridOptions = {
    enableFiltering: true,
    treeRowHeaderAlwaysVisible: false,
    columnDefs: [
      { name: 'name', width: '30%' },
      { name: 'gender', grouping: { groupPriority: 1 }, sort: { priority: 1, direction: 'asc' }, width: '20%', cellFilter: 'mapGender' },
      { name: 'age', treeAggregationType: uiGridGroupingConstants.aggregation.MAX, width: '20%' },
      { name: 'company', width: '25%' },
      { name: 'registered', width: '40%', cellFilter: 'date', type: 'date' },
      { name: 'state', grouping: { groupPriority: 0 }, sort: { priority: 0, direction: 'desc' }, width: '35%', cellTemplate: '<div><div ng-if="!col.grouping || col.grouping.groupPriority === undefined || col.grouping.groupPriority === null || ( row.groupHeader && col.grouping.groupPriority === row.treeLevel )" class="ui-grid-cell-contents" title="TOOLTIP">{{COL_FIELD CUSTOM_FILTERS}}</div></div>' },
      { name: 'balance', width: '25%', cellFilter: 'currency', treeAggregationType: uiGridGroupingConstants.aggregation.AVG, customTreeAggregationFinalizerFn: function( aggregation ) {
        aggregation.rendered = aggregation.value;
      } }
    ],
    onRegisterApi: function( gridApi ) {
      $scope.gridApi = gridApi;
    }
  };

  $http.get('http://ui-grid.info/data/500_complex.json')
    .success(function(data) {
      for ( var i = 0; i < data.length; i++ ){
        var registeredDate = new Date( data[i].registered );
        data[i].state = data[i].address.state;
        data[i].gender = data[i].gender === 'male' ? 1: 2;
        data[i].balance = Number( data[i].balance.slice(1).replace(/,/,'') );
        data[i].registered = new Date( registeredDate.getFullYear(), registeredDate.getMonth(), 1 )
      }
      delete data[2].age;
      $scope.gridOptions.data = data;
    });

  $scope.expandAll = function(){
    $scope.gridApi.treeBase.expandAllRows();
  };

  $scope.toggleRow = function( rowNum ){
    $scope.gridApi.treeBase.toggleRowTreeState($scope.gridApi.grid.renderContainers.body.visibleRowCache[rowNum]);
  };

  $scope.changeGrouping = function() {
    $scope.gridApi.grouping.clearGrouping();
    $scope.gridApi.grouping.groupColumn('age');
    $scope.gridApi.grouping.aggregateColumn('state', uiGridGroupingConstants.aggregation.COUNT);
  };

  $scope.getAggregates = function() {
    var aggregatesTree = [];
    var gender

    var recursiveExtract = function( treeChildren ) {
      return treeChildren.map( function( node ) {
        var newNode = {};
        angular.forEach(node.row.entity, function( attributeCol ) {
          if( typeof(attributeCol.groupVal) !== 'undefined' ) {
            newNode.groupVal = attributeCol.groupVal;
            newNode.aggVal = attributeCol.value;
          }
        });
        newNode.otherAggregations = node.aggregations.map( function( aggregation ) {
          return { colName: aggregation.col.name, value: aggregation.value, type: aggregation.type };
        });
        if( node.children ) {
          newNode.children = recursiveExtract( node.children );
        }
        return newNode;
      });
    }

    aggregatesTree = recursiveExtract( $scope.gridApi.grid.treeBase.tree );

    console.log(aggregatesTree);
  };
}])
.filter('mapGender', function() {
  var genderHash = {
    1: 'male',
    2: 'female'
  };

  return function(input) {
    var result;
    var match;
    if (!input){
      return '';
    } else if (result = genderHash[input]) {
      return result;
    } else if ( ( match = input.match(/(.+)( \(\d+\))/) ) && ( result = genderHash[match[1]] ) ) {
      return result + match[2];
    } else {
      return input;
    }
  };
});

Demo

作者水平有限,不当之处敬请指正。
感谢您的阅读,如果觉得文章对您有帮助,请支持一下。

发表评论

电子邮件地址不会被公开。 必填项已用*标注