原文:Tutorial: 209 Grouping
分组功能允许您根据特定列中的相似值对行进行分组, 在某些方面类似于 excel 数据透视表的效果。不按分组的列可以聚合, 例如, 统计每个组中的行数。
api 文档中提供有”分组” 的功能, 包括分组本身和 treeBase 文档中的共享函数。特别是:
树结构本身被记录在 treeBase。
可以在 columnDef 选项中来设置分组:
grouping: { groupPriority: 0 }
或为列中的聚合设置 treeAggregation: {type: uiGridGroupingConstants.aggregation.COUNT}.
默认情况下分组列会显示在前面, 这将提供更直观的效果。为了避免对pinning 的依赖关系, 是将分组的列作为整体来移动的, 而不是使用pinning 功能。
分组有排序功能, 允许用户更改排序顺序或使用外部排序功能, 并将结果分组。标记为分组的列排序优先级最高。
任何分组的列都有 suppressRemoveSort 设置, 当列被取消分组时, suppressRemoveSort 将返回到 columnDef 中的值。
分组和聚合应与筛选一起使用, 它应只对筛选的行进行分组和聚合。
无法编辑组页眉行, 如果使用选择功能, 则无法选择。但是, 它们可以被导出。
默认情况下, 组 rowHeader 始终可见。如果希望 groupRowHeader 仅在至少一列分组时出现, 则可以在gridOption中设置 treeRowHeaderAlwaysVisible: false
表格初始化之后更改分组, 可以调用以下方法:
以下几点需要注意:
- 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
<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="/release/ui-grid.js">script>
<script src="/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 Allbutton>
<button id="toggleFirstRow" type="button" class="btn btn-success" ng-click="toggleRow(0)">Toggle First Rowbutton>
<button id="toggleSecondRow" type="button" class="btn btn-success" ng-click="toggleRow(1)">Toggle Second Rowbutton>
<button id="changeGrouping" type="button" class="btn btn-success" ng-click="changeGrouping()">Change Groupingbutton>
<button id="getAggregates" type="button" class="btn btn-success" ng-click="getAggregates()">Get Aggregatesbutton>
<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: '{{COL_FIELD CUSTOM_FILTERS}}' },
{ 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('/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
作者水平有限,不当之处敬请指正。
感谢您的阅读,如果觉得文章对您有帮助,请支持一下。