Skip to content

Commit d023360

Browse files
committed
feat(Core): Implementation for #2483. Adds a showGridFooter option that will create a grid footer with the Total Items and Shown Items displayed. Also shows Selected Items if using Selection feature.
BREAKING CHANGE: showFooter option renamed to showColumnFooter; footerRowHeight option renamed to columnFooterHeight
1 parent 30df7d4 commit d023360

21 files changed

+295
-70
lines changed

misc/site/index.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ <h4 class="feature-heading">Complex Example</h4>
245245

246246
$rootScope.gridOptionsComplex = {
247247
enableFiltering: true,
248-
showFooter: true,
248+
showGridFooter: true,
249+
showColumnFooter: true,
249250
columnDefs: [
250251
{ name: 'name', aggregationType: uiGridConstants.aggregationTypes.count, width: 150 },
251252
{ name: 'gender', filter: { term: 'male' }, width: 150, enableCellEdit: false,

misc/tutorial/105_footer.ngdoc

+15-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
@name Tutorial: 105 Footer
33
@description
44

5-
The grid supports a footer row, which can be displayed if showFooterRow option is set to true.
5+
### Grid Footer
6+
The grid supports a grid footer row, which can be displayed if showGridFooter option is set to true (default=false). This footer displays
7+
Total rows in the grid and the number of filtered rows. Selected rows will be shown if using ui-grid-selection feature.
8+
<br/>
9+
### Column Footer
10+
The grid also has column footer, which can be displayed if showColumnFooter option is set to true (default=false).
611
You can set an aggregation function for each column or use a custom footer template to display
712
what ever aggregation you wish.
813
Aggregation functions supported are: sum, avg, row count, min, max.
@@ -23,18 +28,22 @@ You can override the default grid footer template with gridOptions.footerTemplat
2328
var data = [];
2429

2530
$scope.gridOptions = {
26-
showFooter: true,
31+
showGridFooter: true,
32+
showColumnFooter: true,
2733
enableFiltering: true,
2834
columnDefs: [
29-
{ field: 'name', aggregationType: uiGridConstants.aggregationTypes.count, width: '13%' },
35+
{ field: 'name', width: '13%' },
3036
{ field: 'address.street',aggregationType: uiGridConstants.aggregationTypes.sum, width: '13%' },
3137
{ field: 'age', aggregationType: uiGridConstants.aggregationTypes.avg, aggregationHideLabel: true, width: '13%' },
3238
{ name: 'ageMin', field: 'age', aggregationType: uiGridConstants.aggregationTypes.min, width: '13%', displayName: 'Age for min' },
3339
{ name: 'ageMax', field: 'age', aggregationType: uiGridConstants.aggregationTypes.max, width: '13%', displayName: 'Age for max' },
3440
{ name: 'customCellTemplate', field: 'age', width: '14%', footerCellTemplate: '<div class="ui-grid-cell-contents" style="background-color: Red;color: White">custom template</div>' },
3541
{ name: 'registered', field: 'registered', width: '20%', cellFilter: 'date', footerCellFilter: 'date', aggregationType: uiGridConstants.aggregationTypes.max }
3642
],
37-
data: data
43+
data: data,
44+
onRegisterApi: function(gridApi) {
45+
$scope.gridApi = gridApi;
46+
}
3847
}
3948

4049
$http.get('/data/500_complex.json')
@@ -48,7 +57,8 @@ You can override the default grid footer template with gridOptions.footerTemplat
4857
</file>
4958
<file name="index.html">
5059
<div ng-controller="MainCtrl">
51-
<button id="footerButton" class="btn btn-success" ng-click="gridOptions.showFooter = !gridOptions.showFooter">Toggle Footer</button>
60+
<button id="footerButton" class="btn btn-success" ng-click="gridOptions.showGridFooter = !gridOptions.showGridFooter; $scope.gridApi.core.notifyDataChange($scope.gridApi.grid, uiGridConstants.dataChange.OPTIONS);">Toggle Grid Footer</button>
61+
<button class="btn btn-success" ng-click="gridOptions.showColumnFooter = !gridOptions.showColumnFooter; $scope.gridApi.core.notifyDataChange($scope.gridApi.grid, uiGridConstants.dataChange.OPTIONS);">Toggle Column Footer</button>
5262
<div id="grid1" ui-grid="gridOptions" class="grid"></div>
5363
</div>
5464
</file>

misc/tutorial/210_selection.ngdoc

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ Two examples are provided, the first with rowHeaderSelection and multi-select, t
4747
$scope.gridOptions = {
4848
enableRowSelection: true,
4949
enableSelectAll: true,
50-
selectionRowHeaderWidth: 35
50+
selectionRowHeaderWidth: 35,
51+
showGridFooter:true
5152
};
5253

5354
$scope.gridOptions.columnDefs = [

misc/tutorial/401_AllFeatures.ngdoc

+7-3
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ All features are enabled to get an idea of performance
1313
<file name="app.js">
1414
var app = angular.module('app', ['ngTouch', 'ui.grid', 'ui.grid.cellNav', 'ui.grid.edit', 'ui.grid.resizeColumns', 'ui.grid.pinning', 'ui.grid.selection', 'ui.grid.moveColumns']);
1515

16-
app.controller('MainCtrl', ['$scope', '$http', '$timeout', '$interval', function ($scope, $http, $timeout, $interval) {
16+
app.controller('MainCtrl', ['$scope', '$http', '$timeout', '$interval', 'uiGridConstants',
17+
function ($scope, $http, $timeout, $interval, uiGridConstants) {
1718

1819
$scope.gridOptions = {};
1920
$scope.gridOptions.data = 'myData';
2021
$scope.gridOptions.enableColumnResizing = true;
2122
$scope.gridOptions.enableFiltering = true;
2223
$scope.gridOptions.enableGridMenu = true;
23-
24+
$scope.gridOptions.showGridFooter = true;
25+
$scope.gridOptions.showColumnFooter = true;
26+
2427
$scope.gridOptions.rowIdentity = function(row) {
2528
return row.id;
2629
};
@@ -31,7 +34,8 @@ All features are enabled to get an idea of performance
3134
$scope.gridOptions.columnDefs = [
3235
{ name:'id', width:50 },
3336
{ name:'name', width:100 },
34-
{ name:'age', width:100, enableCellEdit: true, cellTemplate: '<div class="ui-grid-cell-contents"><span>Age:{{COL_FIELD}}</span></div>' },
37+
{ name:'age', width:100, enableCellEdit: true, aggregationType:uiGridConstants.aggregationTypes.avg,
38+
cellTemplate: '<div class="ui-grid-cell-contents"><span>Age:{{COL_FIELD}}</span></div>' },
3539
{ name:'address.street', width:150, enableCellEdit: true, cellTemplate: '<div class="ui-grid-cell-contents"><span>Street:{{COL_FIELD}}</span></div>' },
3640
{ name:'address.city', width:150, enableCellEdit: true, cellTemplate: '<div class="ui-grid-cell-contents"><span>City:{{COL_FIELD}}</span></div>' },
3741
{ name:'address.state', width:50, enableCellEdit: true, cellTemplate: '<div class="ui-grid-cell-contents"><span>State:{{COL_FIELD}}</span></div>' },

src/features/selection/js/selection.js

+120-24
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,58 @@
2727
selectionRowHeaderColName: 'selectionRowHeaderCol'
2828
});
2929

30+
//add methods to GridRow
31+
angular.module('ui.grid').config(['$provide', function($provide) {
32+
$provide.decorator('GridRow', ['$delegate', function($delegate) {
33+
34+
/**
35+
* @ngdoc object
36+
* @name ui.grid.selection.api:GridRow
37+
*
38+
* @description GridRow prototype functions added for selection
39+
*/
40+
41+
/**
42+
* @ngdoc object
43+
* @name enableSelection
44+
* @propertyOf ui.grid.selection.api:GridRow
45+
* @description Enable row selection for this row, only settable by internal code.
46+
*
47+
* The grouping feature, for example, might set group header rows to not be selectable.
48+
* <br/>Defaults to true
49+
*/
50+
51+
/**
52+
* @ngdoc object
53+
* @name isSelected
54+
* @propertyOf ui.grid.selection.api:GridRow
55+
* @description Selected state of row. Should be readonly. Make any changes to selected state using setSelected().
56+
* <br/>Defaults to false
57+
*/
58+
59+
60+
/**
61+
* @ngdoc function
62+
* @name setSelected
63+
* @methodOf ui.grid.selection.api:GridRow
64+
* @description Sets the isSelected property and updates the selectedCount
65+
* Changes to isSelected state should only be made via this function
66+
* @param {bool} selelected value to set
67+
*/
68+
$delegate.prototype.setSelected = function(selected) {
69+
this.isSelected = selected;
70+
if (selected) {
71+
this.grid.selection.selectedCount++;
72+
}
73+
else {
74+
this.grid.selection.selectedCount--;
75+
}
76+
};
77+
78+
return $delegate;
79+
}]);
80+
}]);
81+
3082
/**
3183
* @ngdoc service
3284
* @name ui.grid.selection.service:uiGridSelectionService
@@ -40,11 +92,28 @@
4092

4193
initializeGrid: function (grid) {
4294

43-
//add feature namespace and any properties to grid for needed state
95+
//add feature namespace and any properties to grid for needed
96+
/**
97+
* @ngdoc object
98+
* @name ui.grid.selection.grid:selection
99+
*
100+
* @description Grid properties and functions added for selection
101+
*/
44102
grid.selection = {};
45103
grid.selection.lastSelectedRow = null;
46104
grid.selection.selectAll = false;
47105

106+
107+
/**
108+
* @ngdoc object
109+
* @name selectedCount
110+
* @propertyOf ui.grid.selection.grid:selection
111+
* @description Current count of selected rows
112+
* @example
113+
* var count = grid.selection.selectedCount
114+
*/
115+
grid.selection.selectedCount = 0;
116+
48117
service.defaultGridOptions(grid.options);
49118

50119
/**
@@ -157,7 +226,7 @@
157226
var changedRows = [];
158227
grid.rows.forEach(function (row) {
159228
if ( !row.isSelected && row.enableSelection !== false ){
160-
row.isSelected = true;
229+
row.setSelected(true);
161230
service.decideRaiseSelectionEvent( grid, row, changedRows, evt );
162231
}
163232
});
@@ -180,12 +249,12 @@
180249
grid.rows.forEach(function (row) {
181250
if (row.visible) {
182251
if (!row.isSelected && row.enableSelection !== false){
183-
row.isSelected = true;
252+
row.setSelected(true);
184253
service.decideRaiseSelectionEvent( grid, row, changedRows, evt );
185254
}
186255
} else {
187256
if (row.isSelected){
188-
row.isSelected = false;
257+
row.setSelected(false);
189258
service.decideRaiseSelectionEvent( grid, row, changedRows, evt );
190259
}
191260
}
@@ -276,23 +345,6 @@
276345
* set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
277346
*/
278347

279-
/**
280-
* @ngdoc object
281-
* @name ui.grid.selection.api:GridRow
282-
*
283-
* @description GridRow options for selection feature
284-
*/
285-
/**
286-
* @ngdoc object
287-
* @name enableSelection
288-
* @propertyOf ui.grid.selection.api:GridRow
289-
* @description Enable row selection for this row, only settable by internal code.
290-
*
291-
* The grouping feature, for example, might set group header rows to not be selectable.
292-
* <br/>Defaults to true
293-
*/
294-
295-
296348
/**
297349
* @ngdoc object
298350
* @name enableRowSelection
@@ -363,6 +415,16 @@
363415
* <br/>Defaults to 30px
364416
*/
365417
gridOptions.selectionRowHeaderWidth = angular.isDefined(gridOptions.selectionRowHeaderWidth) ? gridOptions.selectionRowHeaderWidth : 30;
418+
419+
/**
420+
* @ngdoc object
421+
* @name enableFooterTotalSelected
422+
* @propertyOf ui.grid.selection.api:GridOptions
423+
* @description Shows the total number of selected items in footer if true.
424+
* <br/>Defaults to true.
425+
* <br/>GridOptions.showFooter must also be set to true.
426+
*/
427+
gridOptions.enableFooterTotalSelected = gridOptions.enableFooterTotalSelected !== false;
366428
},
367429

368430
/**
@@ -392,7 +454,7 @@
392454
if (selected && noUnselect){
393455
// don't deselect the row
394456
} else if (row.enableSelection !== false) {
395-
row.isSelected = !selected;
457+
row.setSelected(!selected);
396458
if (row.isSelected === true) {
397459
grid.selection.lastSelectedRow = row;
398460
} else {
@@ -430,7 +492,7 @@
430492
var rowToSelect = grid.renderContainers.body.visibleRowCache[i];
431493
if (rowToSelect) {
432494
if ( !rowToSelect.isSelected && rowToSelect.enableSelection !== false ){
433-
rowToSelect.isSelected = true;
495+
rowToSelect.setSelected(true);
434496
grid.selection.lastSelectedRow = rowToSelect;
435497
service.decideRaiseSelectionEvent( grid, rowToSelect, changedRows, evt );
436498
}
@@ -463,7 +525,7 @@
463525
var changedRows = [];
464526
service.getSelectedRows(grid).forEach(function (row) {
465527
if ( row.isSelected ){
466-
row.isSelected = false;
528+
row.setSelected(false);
467529
service.decideRaiseSelectionEvent( grid, row, changedRows, evt );
468530
}
469531
});
@@ -757,4 +819,38 @@
757819
};
758820
}]);
759821

822+
module.directive('uiGridGridFooter', ['$compile', 'uiGridConstants', 'gridUtil', function ($compile, uiGridConstants, gridUtil) {
823+
return {
824+
restrict: 'EA',
825+
replace: true,
826+
priority: -1000,
827+
require: '^uiGrid',
828+
scope: true,
829+
compile: function ($elm, $attrs) {
830+
return {
831+
pre: function ($scope, $elm, $attrs, uiGridCtrl) {
832+
833+
if (!uiGridCtrl.grid.options.showGridFooter) {
834+
return;
835+
}
836+
837+
838+
gridUtil.getTemplate('ui-grid/gridFooterSelectedItems')
839+
.then(function (contents) {
840+
var template = angular.element(contents);
841+
842+
var newElm = $compile(template)($scope);
843+
844+
angular.element($elm[0].getElementsByClassName('ui-grid-grid-footer')[0]).append(newElm);
845+
});
846+
},
847+
848+
post: function ($scope, $elm, $attrs, controllers) {
849+
850+
}
851+
};
852+
}
853+
};
854+
}]);
855+
760856
})();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<span ng-if="grid.selection.selectedCount !== 0 && grid.options.enableFooterTotalSelected">({{"search.selectedItems" | t}} {{grid.selection.selectedCount}})</span>

src/features/selection/test/uiGridSelectionService.spec.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe('ui.grid.selection uiGridSelectionService', function () {
1717
$templateCache.put('ui-grid/uiGridCell', '<div/>');
1818
$templateCache.put('ui-grid/editableCell', '<div editable_cell_directive></div>');
1919

20-
grid = gridClassFactory.createGrid({});
20+
grid = gridClassFactory.createGrid({showGridFooter:true});
2121
grid.options.columnDefs = [
2222
{field: 'col1', enableCellEdit: true}
2323
];
@@ -108,6 +108,7 @@ describe('ui.grid.selection uiGridSelectionService', function () {
108108
expect(grid.rows[3].isSelected).toBe(true);
109109
expect(grid.rows[4].isSelected).toBe(true);
110110
expect(grid.rows[5].isSelected).toBe(true);
111+
expect(grid.selection.selectedCount).toBe(4);
111112
});
112113

113114
it('should skip non-selectable rows', function () {
@@ -240,6 +241,7 @@ describe('ui.grid.selection uiGridSelectionService', function () {
240241
expect(grid.rows[8].isSelected).toBe(undefined);
241242
expect(grid.rows[9].isSelected).toBe(true);
242243
expect(grid.selection.selectAll).toBe(true);
244+
expect(grid.selection.selectedCount).toBe(8);
243245
});
244246
});
245247

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(function () {
2+
'use strict';
3+
4+
angular.module('ui.grid').directive('uiGridGridFooter', ['$templateCache', '$compile', 'uiGridConstants', 'gridUtil', '$timeout', function ($templateCache, $compile, uiGridConstants, gridUtil, $timeout) {
5+
var defaultTemplate = 'ui-grid/ui-grid-grid-footer';
6+
7+
return {
8+
restrict: 'EA',
9+
replace: true,
10+
// priority: 1000,
11+
require: '^uiGrid',
12+
scope: true,
13+
compile: function ($elm, $attrs) {
14+
return {
15+
pre: function ($scope, $elm, $attrs, uiGridCtrl) {
16+
17+
$scope.grid = uiGridCtrl.grid;
18+
$scope.getExternalScopes = uiGridCtrl.getExternalScopes;
19+
20+
var footerTemplate = ($scope.grid.options.gridFooterTemplate) ? $scope.grid.options.gridFooterTemplate : defaultTemplate;
21+
gridUtil.getTemplate(footerTemplate)
22+
.then(function (contents) {
23+
var template = angular.element(contents);
24+
25+
var newElm = $compile(template)($scope);
26+
$elm.append(newElm);
27+
});
28+
},
29+
30+
post: function ($scope, $elm, $attrs, controllers) {
31+
32+
}
33+
};
34+
}
35+
};
36+
}]);
37+
38+
})();

src/js/core/directives/ui-grid-native-scrollbar.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@
122122
var w = colContainer.getCanvasWidth();
123123

124124
var bottom = gridBottomBorder;
125-
if (grid.options.showFooter) {
125+
if (grid.hasFooter()) {
126126
bottom -= 1;
127127
}
128128

0 commit comments

Comments
 (0)