Skip to content

Commit bdb832a

Browse files
caseyjholmportuga
authored andcommitted
feat(filter): add filterContainer option to allow a column filter to be placed in column menu
filterContainer has a default value of "headerCell" which is the same as existing behavior. A filterContainer value of "columnMenu" moves the filter into the column menu. This improves the appearance of the filter in situations where there are multiple filters for a single column (preventing the header from simply getting taller and taller) and offers a streamlined alternative to placing the filter directly in the header cell. Fixes #3989.
1 parent 579adf0 commit bdb832a

File tree

8 files changed

+78
-35
lines changed

8 files changed

+78
-35
lines changed

src/js/core/directives/ui-grid-header-cell.js

+44-31
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
(function() {
22
'use strict';
33

4-
angular.module('ui.grid').directive('uiGridHeaderCell', ['$compile', '$timeout', '$window', '$document', 'gridUtil', 'uiGridConstants', 'ScrollEvent', 'i18nService',
5-
function ($compile, $timeout, $window, $document, gridUtil, uiGridConstants, ScrollEvent, i18nService) {
4+
angular.module('ui.grid').directive('uiGridHeaderCell', ['$compile', '$timeout', '$window', '$document', 'gridUtil', 'uiGridConstants', 'ScrollEvent', 'i18nService', '$rootScope',
5+
function ($compile, $timeout, $window, $document, gridUtil, uiGridConstants, ScrollEvent, i18nService, $rootScope) {
66
// Do stuff after mouse has been down this many ms on the header cell
77
var mousedownTimeout = 500,
88
changeModeTimeout = 500; // length of time between a touch event and a mouse event being recognised again, and vice versa
@@ -226,6 +226,35 @@
226226
}
227227
};
228228

229+
var setFilter = function (updateFilters) {
230+
if ( updateFilters ) {
231+
if ( typeof($scope.col.updateFilters) !== 'undefined' ) {
232+
$scope.col.updateFilters($scope.col.filterable);
233+
}
234+
235+
// if column is filterable add a filter watcher
236+
if ($scope.col.filterable) {
237+
$scope.col.filters.forEach( function(filter, i) {
238+
filterDeregisters.push($scope.$watch('col.filters[' + i + '].term', function(n, o) {
239+
if (n !== o) {
240+
uiGridCtrl.grid.api.core.raise.filterChanged();
241+
uiGridCtrl.grid.api.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
242+
uiGridCtrl.grid.queueGridRefresh();
243+
}
244+
}));
245+
});
246+
$scope.$on('$destroy', function() {
247+
filterDeregisters.forEach( function(filterDeregister) {
248+
filterDeregister();
249+
});
250+
});
251+
} else {
252+
filterDeregisters.forEach( function(filterDeregister) {
253+
filterDeregister();
254+
});
255+
}
256+
}
257+
};
229258

230259
var updateHeaderOptions = function() {
231260
var contents = $elm;
@@ -254,36 +283,12 @@
254283
$scope.sortable = Boolean($scope.col.enableSorting);
255284

256285
// Figure out whether this column is filterable or not
257-
var oldFilterable = $scope.filterable;
258-
$scope.filterable = Boolean(uiGridCtrl.grid.options.enableFiltering && $scope.col.enableFiltering);
259-
260-
if ( oldFilterable !== $scope.filterable) {
261-
if ( typeof($scope.col.updateFilters) !== 'undefined' ) {
262-
$scope.col.updateFilters($scope.filterable);
263-
}
286+
var oldFilterable = $scope.col.filterable;
287+
$scope.col.filterable = Boolean(uiGridCtrl.grid.options.enableFiltering && $scope.col.enableFiltering);
264288

265-
// if column is filterable add a filter watcher
266-
if ($scope.filterable) {
267-
$scope.col.filters.forEach( function(filter, i) {
268-
filterDeregisters.push($scope.$watch('col.filters[' + i + '].term', function(n, o) {
269-
if (n !== o) {
270-
uiGridCtrl.grid.api.core.raise.filterChanged();
271-
uiGridCtrl.grid.api.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
272-
uiGridCtrl.grid.queueGridRefresh();
273-
}
274-
}));
275-
});
276-
$scope.$on('$destroy', function() {
277-
filterDeregisters.forEach( function(filterDeregister) {
278-
filterDeregister();
279-
});
280-
});
281-
} else {
282-
filterDeregisters.forEach( function(filterDeregister) {
283-
filterDeregister();
284-
});
285-
}
286-
}
289+
$scope.$applyAsync(function () {
290+
setFilter(oldFilterable !== $scope.col.filterable);
291+
});
287292

288293
// figure out whether we support column menus
289294
$scope.colMenu = ($scope.col.grid.options && $scope.col.grid.options.enableColumnMenus !== false &&
@@ -328,6 +333,14 @@
328333

329334
updateHeaderOptions();
330335

336+
if ($scope.col.filterContainer === 'columnMenu' && $scope.col.filterable) {
337+
$rootScope.$on('menu-shown', function() {
338+
$scope.$applyAsync(function () {
339+
setFilter($scope.col.filterable);
340+
});
341+
});
342+
}
343+
331344
// Register a data change watch that would get triggered whenever someone edits a cell or modifies column defs
332345
var dataChangeDereg = $scope.grid.registerDataChangeCallback( updateHeaderOptions, [uiGridConstants.dataChange.COLUMN]);
333346

src/js/core/directives/ui-grid-menu.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ function ($compile, $timeout, $window, $document, gridUtil, uiGridConstants, i18
3737
scope: {
3838
// shown: '&',
3939
menuItems: '=',
40-
autoHide: '=?'
40+
autoHide: '=?',
41+
col: '=?'
4142
},
4243
require: '?^uiGrid',
4344
templateUrl: 'ui-grid/uiGridMenu',
@@ -157,8 +158,15 @@ function ($compile, $timeout, $window, $document, gridUtil, uiGridConstants, i18
157158

158159

159160
// *** Auto hide when click elsewhere ******
160-
var applyHideMenu = function() {
161+
var applyHideMenu = function(event) {
161162
if ($scope.shown) {
163+
if ($scope.col && $scope.col.filterContainer === 'columnMenu') {
164+
var elm = document.querySelector('.ui-grid-column-menu').querySelector('[ui-grid-filter]');
165+
if (elm && elm.contains(event.target)) {
166+
return false;
167+
}
168+
}
169+
162170
$scope.$apply(function () {
163171
$scope.hideMenu();
164172
});

src/js/core/factories/GridColumn.js

+3
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,9 @@ angular.module('ui.grid')
751751
// Turn on filtering by default (it's disabled by default at the Grid level)
752752
self.enableFiltering = typeof(colDef.enableFiltering) !== 'undefined' ? colDef.enableFiltering : true;
753753

754+
// Place the filter in the header cell by default
755+
self.filterContainer = typeof(colDef.filterContainer) !== 'undefined' ? colDef.filterContainer : self.grid.options.filterContainer;
756+
754757
// self.menuItems = colDef.menuItems;
755758
self.setPropertyOrDefault(colDef, 'menuItems', []);
756759

src/js/core/factories/GridOptions.js

+11
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,17 @@ angular.module('ui.grid')
387387
*/
388388
baseOptions.enableFiltering = baseOptions.enableFiltering === true;
389389

390+
/**
391+
* @ngdoc string
392+
* @name filterContainer
393+
* @propertyOf ui.grid.class:GridOptions
394+
* @description Sets the parent element for the column filter. `headerCell` places
395+
* it in the header cell. `columnMenu` places it in the column menu.
396+
* Can be changed for individual columns using the columnDefs.
397+
* Defaults to `headerCell`
398+
*/
399+
baseOptions.filterContainer = typeof(baseOptions.filterContainer) !== "undefined" ? baseOptions.filterContainer : "headerCell";
400+
390401
/**
391402
* @ngdoc boolean
392403
* @name enableColumnMenus

src/templates/ui-grid/uiGridColumnMenu.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div class="ui-grid-column-menu">
2-
<div ui-grid-menu menu-items="menuItems">
2+
<div ui-grid-menu menu-items="menuItems" col="col">
33
<!-- <div class="ui-grid-column-menu">
44
<div class="inner" ng-show="menuShown">
55
<ul>

src/templates/ui-grid/uiGridHeaderCell.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,5 @@
4646
</i>
4747
</div>
4848

49-
<div ui-grid-filter></div>
49+
<div ui-grid-filter ng-if="col.filterContainer === 'headerCell'"></div>
5050
</div>

src/templates/ui-grid/uiGridMenu.html

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
leave-open="item.leaveOpen"
2929
screen-reader-only="item.screenReaderOnly">
3030
</li>
31+
<li ng-if="col.filterable && col.filterContainer === 'columnMenu'">
32+
<div ui-grid-filter></div>
33+
</li>
3134
</ul>
3235
</div>
3336
</div>

test/unit/core/factories/GridOptions.spec.js

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ describe('GridOptions factory', function () {
4040
scrollDebounce: 300,
4141
enableSorting: true,
4242
enableFiltering: false,
43+
filterContainer: 'headerCell',
4344
enableColumnMenus: true,
4445
enableVerticalScrollbar: 1,
4546
enableHorizontalScrollbar: 1,
@@ -87,6 +88,7 @@ describe('GridOptions factory', function () {
8788
wheelScrollThrottle: 75,
8889
enableSorting: true,
8990
enableFiltering: true,
91+
filterContainer: 'columnMenu',
9092
enableColumnMenus: true,
9193
enableVerticalScrollbar: 1,
9294
enableHorizontalScrollbar: 1,
@@ -132,6 +134,7 @@ describe('GridOptions factory', function () {
132134
scrollDebounce: 300,
133135
enableSorting: true,
134136
enableFiltering: true,
137+
filterContainer: 'columnMenu',
135138
enableColumnMenus: true,
136139
enableVerticalScrollbar: 1,
137140
enableHorizontalScrollbar: 1,
@@ -179,6 +182,7 @@ describe('GridOptions factory', function () {
179182
aggregationCalcThrottle: 1000,
180183
wheelScrollThrottle: 75,
181184
enableFiltering: false,
185+
filterContainer: 'columnMenu',
182186
enableSorting: false,
183187
enableColumnMenus: false,
184188
enableVerticalScrollbar: 0,
@@ -224,6 +228,7 @@ describe('GridOptions factory', function () {
224228
scrollDebounce: 300,
225229
enableSorting: false,
226230
enableFiltering: false,
231+
filterContainer: 'columnMenu',
227232
enableColumnMenus: false,
228233
enableVerticalScrollbar: 0,
229234
enableHorizontalScrollbar: 0,

0 commit comments

Comments
 (0)