Skip to content

Commit b0d943a

Browse files
committed
feat(saveState): add pinning to save state
The idea was to update saveState to include pinning. This turned out to be more involved than initial anticipated. I ended up having to update the pinning feature to include a public event and method so saveState could include pinning as an option. I updated documentation as I worked through it and even updated the tutorial to include pinning. This is in reference to an feature request I made recently #3123.
1 parent 949013c commit b0d943a

File tree

6 files changed

+318
-32
lines changed

6 files changed

+318
-32
lines changed

misc/demo/grid-save.html

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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="https://code.jquery.com/jquery-1.11.1.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+
</style>
26+
</head>
27+
<body ng-controller="Main">
28+
29+
<h2>Grid</h2>
30+
<div ui-grid="gridOptions" class="grid" ui-grid-save-state ui-grid-selection ui-grid-cellNav ui-grid-resize-columns ui-grid-move-columns ui-grid-pinning ></div>
31+
<button id="save" type="button" class="btn btn-success" ng-click="saveState()">Save</button>
32+
<button id="restore" type="button" class="btn btn-success" ng-click="restoreState()">Restore</button>
33+
34+
<br>
35+
<br>
36+
37+
<script>
38+
var app = angular.module('test', ['ui.grid', 'ui.grid.pinning', 'ui.grid.resizeColumns', 'ui.grid.saveState']);
39+
app.controller('Main', function($scope, $http) {
40+
$scope.gridOptions = {};
41+
$scope.gridOptions.columnDefs = [
42+
{ name:'id', width:50 },
43+
{ name:'name', width:100, pinnedLeft:true },
44+
{ name:'age', width:100, pinnedRight:true },
45+
{ name:'address.street', width:150 },
46+
{ name:'address.city', width:150 },
47+
{ name:'address.state', width:50 },
48+
{ name:'address.zip', width:50 },
49+
{ name:'company', width:100 },
50+
{ name:'email', width:100 },
51+
{ name:'phone', width:200 },
52+
{ name:'about', width:300 },
53+
{ name:'friends[0].name', displayName:'1st friend', width:150 },
54+
{ name:'friends[1].name', displayName:'2nd friend', width:150 },
55+
{ name:'friends[2].name', displayName:'3rd friend', width:150 }
56+
];
57+
$scope.gridOptions.enableFiltering = true;
58+
$scope.gridOptions.onRegisterApi = function(gridApi) {
59+
$scope.gridApi = gridApi;
60+
61+
gridApi.pinning.on.columnPinned($scope, function(col, action) {
62+
console.log(col, action);
63+
});
64+
};
65+
$scope.state = {};
66+
67+
$scope.saveState = function() {
68+
$scope.state = $scope.gridApi.saveState.save();
69+
};
70+
71+
$scope.restoreState = function() {
72+
$scope.gridApi.saveState.restore( $scope, $scope.state );
73+
};
74+
75+
$http.get('https://rawgit.com/angular-ui/ui-grid.info/gh-pages/data/500_complex.json')
76+
.success(function(data) {
77+
$scope.gridOptions.data = data;
78+
});
79+
});
80+
</script>
81+
</body>
82+
</html>
83+

misc/tutorial/208_save_state.ngdoc

+5-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ default:
3030
- saveVisible
3131
- saveSort
3232
- saveFilter
33+
- savePinning
3334

3435
@example
3536
In this example we provide a button to save the grid state. You can then modify the grid layout
@@ -38,17 +39,17 @@ to something different, and use the restore button to set the grid back the way
3839

3940
<example module="app">
4041
<file name="app.js">
41-
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.saveState', 'ui.grid.selection', 'ui.grid.cellNav', 'ui.grid.resizeColumns', 'ui.grid.moveColumns' ]);
42+
var app = angular.module('app', ['ngAnimate', 'ngTouch', 'ui.grid', 'ui.grid.saveState', 'ui.grid.selection', 'ui.grid.cellNav', 'ui.grid.resizeColumns', 'ui.grid.moveColumns', 'ui.grid.pinning' ]);
4243

4344
app.controller('MainCtrl', ['$scope', '$http', '$interval', function ($scope, $http, $interval) {
4445
$scope.gridOptions = {
4546
saveFocus: false,
4647
saveScroll: true,
47-
enableFiltering: true,
4848
onRegisterApi: function(gridApi){
4949
$scope.gridApi = gridApi;
5050
}
5151
};
52+
$scope.gridOptions.enableFiltering = true;
5253
$scope.state = {};
5354

5455
$scope.saveState = function() {
@@ -68,7 +69,7 @@ to something different, and use the restore button to set the grid back the way
6869

6970
<file name="index.html">
7071
<div ng-controller="MainCtrl">
71-
<div id="grid1" ui-grid="gridOptions" ui-grid-save-state ui-grid-selection ui-grid-cellNav ui-grid-resize-columns ui-grid-move-columns class="grid"></div>
72+
<div id="grid1" ui-grid="gridOptions" ui-grid-save-state ui-grid-selection ui-grid-cellNav ui-grid-resize-columns ui-grid-move-columns ui-grid-pinning class="grid"></div>
7273
<button id="save" type="button" class="btn btn-success" ng-click="saveState()">Save</button>
7374
<button id="restore" type="button" class="btn btn-success" ng-click="restoreState()">Restore</button>
7475
</div>
@@ -80,6 +81,7 @@ to something different, and use the restore button to set the grid back the way
8081
height: 400px;
8182
}
8283
</file>
84+
8385
<file name="scenario.js">
8486
var gridTestUtils = require('../../test/e2e/gridTestUtils.spec.js');
8587
describe( '208 save state', function() {

src/features/pinning/js/pinning.js

+86-14
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,70 @@
1616

1717
var module = angular.module('ui.grid.pinning', ['ui.grid']);
1818

19-
module.service('uiGridPinningService', ['gridUtil', 'GridRenderContainer', 'i18nService', function (gridUtil, GridRenderContainer, i18nService) {
19+
module.constant('uiGridPinningConstants', {
20+
container: {
21+
LEFT: 'left',
22+
RIGHT: 'right',
23+
NONE: ''
24+
}
25+
});
26+
27+
module.service('uiGridPinningService', ['gridUtil', 'GridRenderContainer', 'i18nService', 'uiGridPinningConstants', function (gridUtil, GridRenderContainer, i18nService, uiGridPinningConstants) {
2028
var service = {
2129

2230
initializeGrid: function (grid) {
2331
service.defaultGridOptions(grid.options);
2432

2533
// Register a column builder to add new menu items for pinning left and right
2634
grid.registerColumnBuilder(service.pinningColumnBuilder);
35+
36+
/**
37+
* @ngdoc object
38+
* @name ui.grid.pinning.api:PublicApi
39+
*
40+
* @description Public Api for pinning feature
41+
*/
42+
var publicApi = {
43+
events: {
44+
pinning: {
45+
/**
46+
* @ngdoc event
47+
* @name columnPin
48+
* @eventOf ui.grid.pinning.api:PublicApi
49+
* @description raised when column pin state has changed
50+
* <pre>
51+
* gridApi.pinning.on.columnPinned(scope, function(colDef){})
52+
* </pre>
53+
* @param {object} colDef the column that was changed
54+
* @param {string} container the render container the column is in ('left', 'right', '')
55+
*/
56+
columnPinned: function(colDef, container) {
57+
}
58+
}
59+
},
60+
methods: {
61+
pinning: {
62+
/**
63+
* @ngdoc function
64+
* @name pinColumn
65+
* @methodOf ui.grid.pinning.api:PublicApi
66+
* @description pin column left, right, or none
67+
* <pre>
68+
* gridApi.pinning.pinColumn(col, uiGridPinningConstants.container.LEFT)
69+
* </pre>
70+
* @param {gridColumn} col the column being pinned
71+
* @param {string} container one of the recognised types
72+
* from uiGridPinningConstants
73+
*/
74+
pinColumn: function(col, container) {
75+
service.pinColumn(grid, col, container);
76+
}
77+
}
78+
}
79+
};
80+
81+
grid.api.registerEventsFromObject(publicApi.events);
82+
grid.api.registerMethodsFromObject(publicApi.methods);
2783
},
2884

2985
defaultGridOptions: function (gridOptions) {
@@ -124,11 +180,7 @@
124180
return typeof(this.context.col.renderContainer) === 'undefined' || !this.context.col.renderContainer || this.context.col.renderContainer !== 'left';
125181
},
126182
action: function () {
127-
this.context.col.renderContainer = 'left';
128-
this.context.col.width = this.context.col.drawnWidth;
129-
this.context.col.grid.createLeftContainer();
130-
131-
col.grid.refresh();
183+
service.pinColumn(this.context.col.grid, this.context.col, uiGridPinningConstants.container.LEFT);
132184
}
133185
};
134186

@@ -140,11 +192,7 @@
140192
return typeof(this.context.col.renderContainer) === 'undefined' || !this.context.col.renderContainer || this.context.col.renderContainer !== 'right';
141193
},
142194
action: function () {
143-
this.context.col.renderContainer = 'right';
144-
this.context.col.width = this.context.col.drawnWidth;
145-
this.context.col.grid.createRightContainer();
146-
147-
col.grid.refresh();
195+
service.pinColumn(this.context.col.grid, this.context.col, uiGridPinningConstants.container.RIGHT);
148196
}
149197
};
150198

@@ -156,9 +204,7 @@
156204
return typeof(this.context.col.renderContainer) !== 'undefined' && this.context.col.renderContainer !== null && this.context.col.renderContainer !== 'body';
157205
},
158206
action: function () {
159-
this.context.col.renderContainer = null;
160-
161-
col.grid.refresh();
207+
service.pinColumn(this.context.col.grid, this.context.col, uiGridPinningConstants.container.UNPIN);
162208
}
163209
};
164210

@@ -171,6 +217,32 @@
171217
if (!gridUtil.arrayContainsObjectWithProperty(col.menuItems, 'name', 'ui.grid.pinning.unpin')) {
172218
col.menuItems.push(removePinAction);
173219
}
220+
},
221+
222+
pinColumn: function(grid, col, container) {
223+
if (container === uiGridPinningConstants.container.NONE) {
224+
col.renderContainer = null;
225+
}
226+
else {
227+
col.renderContainer = container;
228+
col.width = col.drawnWidth;
229+
if (container === uiGridPinningConstants.container.LEFT) {
230+
grid.createLeftContainer();
231+
}
232+
else if (container === uiGridPinningConstants.container.RIGHT) {
233+
grid.createRightContainer();
234+
}
235+
}
236+
237+
// Need to call refresh twice; once to move our column over to the new render container and then
238+
// a second time to update the grid viewport dimensions with our adjustments
239+
grid.refresh()
240+
.then(function () {
241+
grid.refresh()
242+
.then(function() {
243+
grid.api.pinning.raise.columnPinned( col.colDef, container );
244+
});
245+
});
174246
}
175247
};
176248

src/features/pinning/test/uiGridPinningService.spec.js

+72-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
/* global _ */
22
describe('ui.grid.pinning uiGridPinningService', function () {
33
var uiGridPinningService;
4+
var uiGridPinningConstants;
45
var gridClassFactory;
56
var grid;
67
var GridRenderContainer;
78

89
beforeEach(module('ui.grid.pinning'));
910

10-
beforeEach(inject(function (_uiGridPinningService_,_gridClassFactory_, $templateCache, _GridRenderContainer_) {
11+
beforeEach(inject(function (_uiGridPinningService_,_gridClassFactory_, $templateCache, _GridRenderContainer_, _uiGridPinningConstants_) {
1112
uiGridPinningService = _uiGridPinningService_;
13+
uiGridPinningConstants = _uiGridPinningConstants_;
1214
gridClassFactory = _gridClassFactory_;
1315
GridRenderContainer = _GridRenderContainer_;
1416

@@ -131,4 +133,73 @@ describe('ui.grid.pinning uiGridPinningService', function () {
131133

132134
});
133135

136+
describe('pinColumn', function() {
137+
138+
var previousWidth;
139+
140+
beforeEach(function() {
141+
spyOn(grid, 'createLeftContainer').andCallThrough();
142+
spyOn(grid, 'createRightContainer').andCallThrough();
143+
previousWidth = grid.columns[0].drawnWidth;
144+
});
145+
146+
describe('left', function() {
147+
148+
beforeEach(function() {
149+
grid.api.pinning.pinColumn(grid.columns[0], uiGridPinningConstants.container.LEFT);
150+
});
151+
152+
it('should set renderContainer to be left', function(){
153+
expect(grid.columns[0].renderContainer).toEqual('left');
154+
});
155+
156+
it('should call createLeftContainer', function() {
157+
expect(grid.createLeftContainer).toHaveBeenCalled();
158+
});
159+
160+
it('should set width based on previous setting', function() {
161+
expect(grid.width).toEqual(previousWidth);
162+
});
163+
164+
});
165+
166+
describe('right', function() {
167+
168+
beforeEach(function() {
169+
grid.api.pinning.pinColumn(grid.columns[0], uiGridPinningConstants.container.RIGHT);
170+
});
171+
172+
it('should set renderContainer to be right', function(){
173+
expect(grid.columns[0].renderContainer).toEqual('right');
174+
});
175+
176+
it('should call createLeftContainer', function() {
177+
expect(grid.createRightContainer).toHaveBeenCalled();
178+
});
179+
180+
it('should set width based on previous setting', function() {
181+
expect(grid.width).toEqual(previousWidth);
182+
});
183+
184+
});
185+
186+
describe('none', function() {
187+
188+
beforeEach(function() {
189+
grid.api.pinning.pinColumn(grid.columns[0], uiGridPinningConstants.container.NONE);
190+
});
191+
192+
it('should set renderContainer to be null', function(){
193+
expect(grid.columns[0].renderContainer).toBeNull();
194+
});
195+
196+
it('should NOT call either container creation methods', function() {
197+
expect(grid.createLeftContainer).not.toHaveBeenCalled();
198+
expect(grid.createRightContainer).not.toHaveBeenCalled();
199+
});
200+
201+
});
202+
203+
});
204+
134205
});

0 commit comments

Comments
 (0)