Skip to content

Commit 865573c

Browse files
committed
fix(Grid): Allow col reordering with column defs
Once initialized, the grid was displaying buggy behavior when swapping column definitions in or out. This change fixes that behavior, and allows for Grid.buildColumns() to reorder columns according to the order of columnDefs by supplying an option parameter with the property `orderByColumnDefs` set to true. This also required changing the call to buildColumns() within the grid's dataWatchFunction so that it uses this option. Fixes #1948
1 parent e4b2293 commit 865573c

File tree

8 files changed

+215
-7
lines changed

8 files changed

+215
-7
lines changed

Gruntfile.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,11 @@ module.exports = function(grunt) {
272272
/* Protractor */
273273
browser: false,
274274

275+
/* Lodash */
276+
_: false,
277+
275278
/* jquery (testing only) */
276-
$:false,
279+
$: false,
277280
jQuery: false,
278281

279282

misc/demo/col-swap.html

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<!DOCTYPE html>
2+
<html class="no-js" ng-app="test"><!--<![endif]-->
3+
<head>
4+
<meta charset="utf-8">
5+
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
6+
<title></title>
7+
<meta content="width=device-width" name="viewport">
8+
9+
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" />
10+
<link href="/dist/release/ui-grid.css" rel="stylesheet">
11+
12+
<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
13+
<script src="/lib/test/angular/1.2.26/angular.js"></script>
14+
<script src="/dist/release/ui-grid.js"></script>
15+
16+
<style>
17+
body {
18+
padding: 60px;
19+
min-height: 600px;
20+
}
21+
.grid {
22+
width: 500px;
23+
height: 400px;
24+
}
25+
.placeholder {
26+
height: 50%;
27+
width: 50%;
28+
border: 3px solid black;
29+
background: #ccc;
30+
}
31+
</style>
32+
</head>
33+
<body ng-controller="Main">
34+
<!-- <h1>Test</h1> -->
35+
36+
<!-- <div class="row main"> -->
37+
<h2>Grid</h2>
38+
<button type="button" ng-click="swap()">Swap Columns</button>
39+
<br>
40+
<br>
41+
<div ui-grid="gridOptions" class="grid"></div>
42+
<!-- <div class="placeholder"> -->
43+
<!-- </div> -->
44+
45+
<br>
46+
<br>
47+
48+
<script>
49+
var app = angular.module('test', ['ui.grid']);
50+
app.controller('Main', function($scope, $http) {
51+
var cols1 = [
52+
{ field:'id', width:50 },
53+
{ field:'name', width:100 },
54+
{ field:'age', width:100 }
55+
];
56+
57+
var cols2 = [
58+
{ field:'age', width:100 },
59+
{ field:'name', width:100 },
60+
{ field:'id', width:50 }
61+
];
62+
63+
// var cols1 = [
64+
// { name: 'id', field:'id', width:50 },
65+
// { name: 'name', field:'name', width:100 },
66+
// { name: 'age', field:'age', width:100 }
67+
// ];
68+
69+
// var cols2 = [
70+
// { name: 'age', field:'age', width:100 },
71+
// { name: 'name', field:'name', width:100 },
72+
// { name: 'id', field:'id', width:50 }
73+
// ];
74+
75+
$scope.gridOptions = {};
76+
$scope.gridOptions.columnDefs = cols1;
77+
78+
$http.get('https://rawgit.com/angular-ui/ui-grid.info/gh-pages/data/500_complex.json')
79+
.success(function(data) {
80+
$scope.gridOptions.data = data;
81+
});
82+
83+
$scope.swap = function () {
84+
($scope.gridOptions.columnDefs === cols1) ?
85+
$scope.gridOptions.columnDefs = cols2 :
86+
$scope.gridOptions.columnDefs = cols1;
87+
};
88+
});
89+
</script>
90+
</body>
91+
</html>
92+

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

+10-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ angular.module('ui.grid').directive('uiGridCell', ['$compile', '$parse', 'gridUt
4141
}
4242
},
4343
post: function($scope, $elm, $attrs, uiGridCtrl) {
44-
$elm.addClass($scope.col.getColClass(false));
44+
var initColClass = $scope.col.getColClass(false);
45+
$elm.addClass(initColClass);
4546

4647
var classAdded;
4748
var updateClass = function( grid ){
@@ -74,6 +75,14 @@ angular.module('ui.grid').directive('uiGridCell', ['$compile', '$parse', 'gridUt
7475
if ( classAdded || $scope.col.cellClass ){
7576
updateClass();
7677
}
78+
79+
// See if the column's internal class has changed
80+
var newColClass = $scope.col.getColClass(false);
81+
if (newColClass !== initColClass) {
82+
$elm.removeClass(initColClass);
83+
$elm.addClass(newColClass);
84+
initColClass = newColClass;
85+
}
7786
}
7887
};
7988

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

+14-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030

3131
$scope.renderContainer = uiGridCtrl.grid.renderContainers[renderContainerCtrl.containerId];
3232

33-
$elm.addClass($scope.col.getColClass(false));
33+
var initColClass = $scope.col.getColClass(false);
34+
$elm.addClass(initColClass);
3435

3536
// Hide the menu by default
3637
$scope.menuShown = false;
@@ -65,6 +66,18 @@
6566
var rightMostContainer = $scope.grid.renderContainers['right'] ? $scope.grid.renderContainers['right'] : $scope.grid.renderContainers['body'];
6667
$scope.isLastCol = ( $scope.col === rightMostContainer.visibleColumnCache[ rightMostContainer.visibleColumnCache.length - 1 ] );
6768
};
69+
70+
$scope.$watch('col', function (n, o) {
71+
if (n !== o) {
72+
// See if the column's internal class has changed
73+
var newColClass = $scope.col.getColClass(false);
74+
if (newColClass !== initColClass) {
75+
$elm.removeClass(initColClass);
76+
$elm.addClass(newColClass);
77+
initColClass = newColClass;
78+
}
79+
}
80+
});
6881

6982
updateClass();
7083

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
function columnDefsWatchFunction(n, o) {
5050
if (n && n !== o) {
5151
self.grid.options.columnDefs = n;
52-
self.grid.buildColumns()
52+
self.grid.buildColumns({ orderByColumnDefs: true })
5353
.then(function(){
5454

5555
self.grid.preCompileCellTemplates();

src/js/core/factories/Grid.js

+41-2
Original file line numberDiff line numberDiff line change
@@ -561,9 +561,19 @@ angular.module('ui.grid')
561561
* @methodOf ui.grid.class:Grid
562562
* @description creates GridColumn objects from the columnDefinition. Calls each registered
563563
* columnBuilder to further process the column
564+
* @param {object} options An object contains options to use when building columns
565+
*
566+
* * **orderByColumnDefs**: defaults to **false**. When true, `buildColumns` will reorder existing columns according to the order within the column definitions.
567+
*
564568
* @returns {Promise} a promise to load any needed column resources
565569
*/
566-
Grid.prototype.buildColumns = function buildColumns() {
570+
Grid.prototype.buildColumns = function buildColumns(opts) {
571+
var options = {
572+
orderByColumnDefs: false
573+
};
574+
575+
angular.extend(options, opts);
576+
567577
// gridUtil.logDebug('buildColumns');
568578
var self = this;
569579
var builderPromises = [];
@@ -605,7 +615,36 @@ angular.module('ui.grid')
605615
builderPromises.push(builder.call(self, colDef, col, self.options));
606616
});
607617
});
608-
618+
619+
/*** Reorder columns if necessary ***/
620+
if (!!options.orderByColumnDefs) {
621+
// Create a shallow copy of the columns as a cache
622+
var columnCache = self.columns.slice(0);
623+
624+
// We need to allow for the "row headers" when mapping from the column defs array to the columns array
625+
// If we have a row header in columns[0] and don't account for it we'll overwrite it with the column in columnDefs[0]
626+
var rowHeaderOffset = self.rowHeaderColumns.length;
627+
628+
// Go through all the column defs
629+
for (i = 0; i < self.options.columnDefs.length; i++) {
630+
// If the column at this index has a different name than the column at the same index in the column defs...
631+
if (self.columns[i + rowHeaderOffset].name !== self.options.columnDefs[i].name) {
632+
// Replace the one in the cache with the appropriate column
633+
columnCache[i + rowHeaderOffset] = self.getColumn(self.options.columnDefs[i].name);
634+
}
635+
else {
636+
// Otherwise just copy over the one from the initial columns
637+
columnCache[i + rowHeaderOffset] = self.columns[i + rowHeaderOffset];
638+
}
639+
}
640+
641+
// Empty out the columns array, non-destructively
642+
self.columns.length = 0;
643+
644+
// And splice in the updated, ordered columns from the cache
645+
Array.prototype.splice.apply(self.columns, [0, 0].concat(columnCache));
646+
}
647+
609648
return $q.all(builderPromises).then(function(){
610649
if (self.rows.length > 0){
611650
self.assignTypes();

src/js/core/factories/GridColumn.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ angular.module('ui.grid')
105105
self.grid = grid;
106106
self.uid = uid;
107107

108-
self.updateColumnDef(colDef, true );
108+
self.updateColumnDef(colDef, true);
109109
}
110110

111111

test/unit/core/directives/uiGridCell.spec.js

+52
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,57 @@ describe('uiGridCell', function () {
8686
}));
8787
});
8888

89+
it('should change a columns class when its uid changes', inject(function (gridUtil, $compile, uiGridConstants) {
90+
// Reset the UIDs (used by columns) so they're fresh and clean
91+
gridUtil.resetUids();
92+
93+
// Set up a couple basic columns
94+
$scope.gridOptions = {
95+
columnDefs: [{ field: 'name', width: 100 }, { field: 'age', width: 50 }],
96+
data: [
97+
{ name: 'Bob', age: 50 }
98+
]
99+
};
100+
101+
// Create a grid elements
102+
var gridElm = angular.element('<div ui-grid="gridOptions" style="width: 400px; height: 300px"></div>');
103+
104+
// Compile the grid and attach it to the document, as the widths won't be right if it's unattached
105+
$compile(gridElm)($scope);
106+
document.body.appendChild(gridElm[0]);
107+
$scope.$digest();
108+
109+
// Get the first column and its root column class
110+
var firstCol = $(gridElm).find('.ui-grid-cell').first();
111+
var firstHeaderCell = $(gridElm).find('.ui-grid-header-cell').first();
112+
var classRegEx = new RegExp('^' + uiGridConstants.COL_CLASS_PREFIX);
113+
var class1 = _(firstCol[0].classList).find(function(c) { return classRegEx.test(c); });
114+
115+
// The first column should be 100px wide because we said it should be
116+
expect(firstCol.outerWidth()).toEqual(100, 'first cell is 100px');
117+
expect(firstHeaderCell.innerWidth()).toEqual(100, "header cell is 100px");
118+
119+
// Now swap the columns in the column defs
120+
$scope.gridOptions.columnDefs = [{ field: 'age', width: 50 }, { field: 'name', width: 100 }];
121+
$scope.$digest();
89122

123+
var firstColAgain = $(gridElm).find('.ui-grid-cell').first();
124+
var firstHeaderCellAgain = $(gridElm).find('.ui-grid-header-cell').first();
125+
var class2 = _(firstColAgain[0].classList).find(function(c) { return classRegEx.test(c); });
126+
127+
// The column root classes should have changed
128+
expect(class2).not.toEqual(class1);
129+
130+
// The first column should now be 50px wide
131+
expect(firstColAgain.outerWidth()).toEqual(50, 'first cell again is 50');
132+
expect(firstHeaderCellAgain.innerWidth()).toEqual(50, 'header cell again is 50px');
133+
134+
// ... and the last column should now be 100px wide
135+
var lastCol = $(gridElm).find('.ui-grid-cell').last();
136+
var lastHeaderCell = $(gridElm).find('.ui-grid-header-cell').last();
137+
expect(lastCol.outerWidth()).toEqual(100, 'last cell again is 100px');
138+
expect(lastHeaderCell.innerWidth()).toEqual(100, 'last header cell again is 100px');
139+
140+
angular.element(gridElm).remove();
141+
}));
90142
});

0 commit comments

Comments
 (0)