Skip to content

Commit 42b789a

Browse files
committed
Merge pull request #3529 from PaulL1/treebase
BREAKING treeView uses treeBase, will eventually share with grouping
2 parents 56b12b5 + d8b1a91 commit 42b789a

20 files changed

+2544
-966
lines changed

misc/tutorial/215_treeView.ngdoc

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,40 @@
33
@description The tree view feature allows you to create a tree from your grid, specifying which
44
of your data rows are nodes and which are leaves.
55

6+
**_Note that recent breaking changes have been implemented on treeView and grouping, combining much
7+
of the logic into a treeBase module. This means that you may need to include the treeBase module
8+
in your code, and that some of the events and methods have moved to grid.api.treeBase instead
9+
of grid.api.treeView_**
10+
611
In your data you tell us the nodes by setting the property $$treeLevel on a given row. Levels
712
start at 0 and increase as you move down the tree.
813

914
If you wish to load your tree incrementally, you can listen to the rowExpanded event, which will
1015
tell you whenever a row is expanded. You can then retrieve additional data from the server and
1116
splice it into the data array at the right point, the grid will automatically render the data
12-
when it arrives.
17+
when it arrives.
18+
19+
Treeview allows sorting, and implements it as a recursive tree sort - it sorts the children of
20+
each of the nodes of the tree.
21+
22+
Treeview allows filtering, it filters all the rows (nodes and leaves) and then makes sure that
23+
all parents of any visible row are also visible. Note that filtering doesn't change expand/collapse
24+
states, your users will still need to expand the nodes to get to the filtered rows.
1325

14-
In general it doesn't make sense to allow sorting when you're using the grid as a tree - the
15-
structure of the data is very positional, and if the user were to sort the data it would break
16-
the tree.
26+
Treeview includes aggregation logic, which is implemented by setting the aggregation property on
27+
the columnDef. The aggregation property is documented at {@link api/ui.grid.treeBase.api:ColumnDef columnDef}.
28+
Refer to {@link 319_complex_trees complex trees} for more detail.
1729

1830
TreeView is still alpha, and under development, however it is included in the distribution files
1931
to allow people to start using it. Notable outstandings are:
20-
- calculates but doesn't display counts of child nodes anywhere, it would be nice if it did
2132
- it would be nice to display an hourglass or icon whilst additional data was loading, the current
2233
arrangement means the grid doesn't know whether or not you're adding additional data
23-
- perhaps we could permit sorting of the nodes, and the children within each node, rather than sorting the
24-
whole data set. This would be a whole new sort algorithm though
2534

2635
Options to watch out for include:
2736

28-
- `treeViewIndent`: the expand buttons are indented by a number of pixels (default 10) as the tree
37+
- `treeIndent`: the expand buttons are indented by a number of pixels (default 10) as the tree
2938
level gets deeper. Larger values look nicer
30-
- `treeViewRowHeaderWidth`: the width of the tree row header
39+
- `treeRowHeaderWidth`: the width of the tree row header
3140
- `showTreeExpandNoChildren`: defaults to true. Shows the + even if there are no children, allows
3241
you to dynamically load children. If set to false there'll be no + if there are no children
3342

@@ -37,12 +46,12 @@ are loaded only when that row is expanded. They have a 2 second delay to simula
3746

3847
<example module="app">
3948
<file name="app.js">
40-
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.treeView' ]);
49+
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.treeView', 'ui.grid.treeBase' ]);
4150

42-
app.controller('MainCtrl', ['$scope', '$http', '$interval', 'uiGridTreeViewConstants', function ($scope, $http, $interval, uiGridTreeViewConstants ) {
51+
app.controller('MainCtrl', ['$scope', '$http', '$interval', 'uiGridTreeBaseConstants', function ($scope, $http, $interval, uiGridTreeBaseConstants ) {
4352
$scope.gridOptions = {
44-
enableSorting: false,
45-
enableFiltering: false,
53+
enableSorting: true,
54+
enableFiltering: true,
4655
columnDefs: [
4756
{ name: 'name', width: '30%' },
4857
{ name: 'gender', width: '20%' },
@@ -53,7 +62,7 @@ are loaded only when that row is expanded. They have a 2 second delay to simula
5362
],
5463
onRegisterApi: function( gridApi ) {
5564
$scope.gridApi = gridApi;
56-
$scope.gridApi.treeView.on.rowExpanded($scope, function(row) {
65+
$scope.gridApi.treeBase.on.rowExpanded($scope, function(row) {
5766
if( row.entity.$$hashKey === $scope.gridOptions.data[50].$$hashKey && !$scope.nodeLoaded ) {
5867
$interval(function() {
5968
$scope.gridOptions.data.splice(51,0,
@@ -84,15 +93,15 @@ are loaded only when that row is expanded. They have a 2 second delay to simula
8493
});
8594

8695
$scope.expandAll = function(){
87-
$scope.gridApi.treeView.expandAllRows();
96+
$scope.gridApi.treeBase.expandAllRows();
8897
};
89-
98+
9099
$scope.toggleRow = function( rowNum ){
91-
$scope.gridApi.treeView.toggleRowTreeViewState($scope.gridApi.grid.renderContainers.body.visibleRowCache[rowNum]);
100+
$scope.gridApi.treeBase.toggleRowTreeState($scope.gridApi.grid.renderContainers.body.visibleRowCache[rowNum]);
92101
};
93102
}]);
94103
</file>
95-
104+
96105
<file name="index.html">
97106
<div ng-controller="MainCtrl">
98107
<button id="expandAll" type="button" class="btn btn-success" ng-click="expandAll()">Expand All</button>
@@ -101,7 +110,7 @@ are loaded only when that row is expanded. They have a 2 second delay to simula
101110
<div id="grid1" ui-grid="gridOptions" ui-grid-tree-view class="grid"></div>
102111
</div>
103112
</file>
104-
113+
105114
<file name="main.css">
106115
.grid {
107116
width: 500px;

misc/tutorial/319_complex_trees.ngdoc

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
@ngdoc overview
2+
@name Tutorial: 319 Complex Trees
3+
@description The tree feature offers a number of advanced features, care should be taken in
4+
using these that you understand exactly what you are trying to achieve, and how to utilise
5+
the tree to achieve that.
6+
7+
Firstly, aggregation. The baseTree provides aggregation logic, this logic is used by grouping
8+
to provide totals, averages etc. The aggregation logic works well with grouping, as grouping
9+
creates new blank rows into which the aggregated totals can be written.
10+
11+
With treeView, you can set aggregations, and that aggregation will by default be written into the entity
12+
for the non-leaf nodes, masking (but not replacing) the data in that column for that entity.
13+
14+
Note also that aggregation only aggregates data from leaf nodes - a node with a $$treeLevel will not
15+
be aggregated.
16+
17+
There are therefore a few options for how you use aggregation with treeView:
18+
19+
1. You have a column that has data only for leaf nodes, and therefore this data can be aggregated in
20+
the non-leaf nodes. For example, if you were building a tree representing a file system, the files themselves
21+
might have sizes, and you might want to aggregate these sizes to show folder size. The folders wouldn't have
22+
their own sizes, therefore there is an available space to write the aggregation data
23+
2. You create a custom column that references the same field as an existing column but has it's own
24+
column name. The aggregations are visible in this column, and you work some magic with the cellTemplates
25+
to make this work as you desire
26+
3. You write a custom cellTemplate that shows both the value for that non-leaf node and the aggregated value in
27+
the same cell. These values are in different places in the entity - the original value is in `entity.fieldName`,
28+
the aggregated value is in `entity.$$colUid.value` (refer to the treeBase documentation to get more details from
29+
the aggregation such as the label or the rendered value). You can write a cellTemplate that displays both of these.
30+
4. You set the `aggregationUpdateEntity` flag on the colDef. The data will then be left in `row.treeNode.aggregations`
31+
in an array form. You need to process that array to get the data out. You might use this if you're building
32+
a graph behind the scenes, or something similar. If so, you'd probably look to get the aggregation data out
33+
of grid.treeBase.tree, rather than extracting it from the rows. You can access the subtree under a given
34+
row (if you're building a graph for the selected row) through `row.treeNode`.
35+
36+
TreeView doesn't provide a UI for setting aggregations - unlike grouping you cannot select a column and pick an
37+
aggregation. You can add this UI yourself through custom menu items, but given the above comments
38+
you need to be careful how you use it.
39+
40+
Aggregations are set on the columnDef or column in the format:
41+
```
42+
colDef.aggregation: { type: uiGridTreeBaseConstants.SUM }
43+
```
44+
45+
You can provide a custom aggregation function, but note that the aggregation is performed as a running total, so
46+
your function needs to work within that framework. Refer the documentation for `customAggregationFn` under
47+
{@link api/ui.grid.treeBase.api:ColumnDef columnDef}.
48+
49+
You can provide a custom finalizer function, which can be used to format or otherwise complete your aggregation
50+
once the leaf nodes under a particular node have been aggregated. Refer the documentation for `customAggregationFinalizerFn`
51+
under {@link api/ui.grid.treeBase.api:ColumnDef columnDef}.
52+
53+
@example
54+
In this example the balance field is averaged, and then displayed in the standard column using a custom cellTemplate
55+
that shows both the value for that row as well as the aggregated value.
56+
57+
<example module="app">
58+
<file name="app.js">
59+
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.treeView', 'ui.grid.treeBase' ]);
60+
61+
app.controller('MainCtrl', ['$scope', '$http', '$interval', 'uiGridTreeBaseConstants', function ($scope, $http, $interval, uiGridTreeBaseConstants ) {
62+
$scope.gridOptions = {
63+
enableSorting: true,
64+
enableFiltering: true,
65+
columnDefs: [
66+
{ name: 'name', width: '30%' },
67+
{ name: 'gender', width: '20%' },
68+
{ name: 'age', width: '20%' },
69+
{ name: 'company', width: '25%' },
70+
{ name: 'state', width: '35%', field: 'address.state' },
71+
{ name: 'balance', width: '25%', aggregation: { type: uiGridTreeBaseConstants.aggregation.SUM }, cellFilter: 'currency', cellTemplate: '<div class="ui-grid-cell-contents" title="TOOLTIP"><span>{{row.entity.balance CUSTOM_FILTERS}}</span><span ng-if="row.entity[\'$$\' + col.uid]"> ({{row.entity["$$" + col.uid].value CUSTOM_FILTERS}})</span></div>' }
72+
],
73+
onRegisterApi: function( gridApi ) {
74+
$scope.gridApi = gridApi;
75+
$scope.gridApi.treeBase.on.rowExpanded($scope, function(row) {
76+
if( row.entity.$$hashKey === $scope.gridOptions.data[50].$$hashKey && !$scope.nodeLoaded ) {
77+
$interval(function() {
78+
$scope.gridOptions.data.splice(51,0,
79+
{name: 'Dynamic 1', gender: 'female', age: 53, company: 'Griddable grids', balance: 38000, $$treeLevel: 1},
80+
{name: 'Dynamic 2', gender: 'male', age: 18, company: 'Griddable grids', balance: 29000, $$treeLevel: 1}
81+
);
82+
$scope.nodeLoaded = true;
83+
}, 2000, 1);
84+
}
85+
});
86+
}
87+
};
88+
89+
$http.get('/data/500_complex.json')
90+
.success(function(data) {
91+
for ( var i = 0; i < data.length; i++ ){
92+
data[i].balance = Number( data[i].balance.slice(1).replace(/,/,'') );
93+
}
94+
data[0].$$treeLevel = 0;
95+
data[1].$$treeLevel = 1;
96+
data[10].$$treeLevel = 1;
97+
data[20].$$treeLevel = 0;
98+
data[25].$$treeLevel = 1;
99+
data[50].$$treeLevel = 0;
100+
data[51].$$treeLevel = 0;
101+
$scope.gridOptions.data = data;
102+
});
103+
104+
$scope.expandAll = function(){
105+
$scope.gridApi.treeBase.expandAllRows();
106+
};
107+
108+
$scope.toggleRow = function( rowNum ){
109+
$scope.gridApi.treeBase.toggleRowTreeState($scope.gridApi.grid.renderContainers.body.visibleRowCache[rowNum]);
110+
};
111+
}]);
112+
</file>
113+
114+
<file name="index.html">
115+
<div ng-controller="MainCtrl">
116+
<button id="expandAll" type="button" class="btn btn-success" ng-click="expandAll()">Expand All</button>
117+
<button id="toggleFirstRow" type="button" class="btn btn-success" ng-click="toggleRow(0)">Toggle First Row</button>
118+
<button id="toggleSecondRow" type="button" class="btn btn-success" ng-click="toggleRow(1)">Toggle Second Row</button>
119+
<div id="grid1" ui-grid="gridOptions" ui-grid-tree-view class="grid"></div>
120+
</div>
121+
</file>
122+
123+
<file name="main.css">
124+
.grid {
125+
width: 500px;
126+
height: 400px;
127+
}
128+
</file>
129+
<file name="scenario.js">
130+
var gridTestUtils = require('../../test/e2e/gridTestUtils.spec.js');
131+
describe( '215 tree view', function() {
132+
});
133+
</file>
134+
</example>

0 commit comments

Comments
 (0)