Skip to content

Commit

Permalink
fix(uiGrid): Wait for grid to get dimensions
Browse files Browse the repository at this point in the history
If the grid has no width initially, wait for up to 2 seconds for the grid
to be shown and have dimensions. Once that happens it will re-initialize
its height and width and redraw the canvas.

This should fix the problems with the grid taking up too much space in a
modal.

BREAKING CHANGE: gridUtil will no longer calculate dimensions of hidden
elements
  • Loading branch information
c0bra committed Apr 8, 2015
1 parent 76029e7 commit e7dfb8c
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 108 deletions.
70 changes: 70 additions & 0 deletions misc/demo/modal.html
@@ -0,0 +1,70 @@
<!DOCTYPE html>
<html ng-app="app">

<head>
<link data-require="bootstrap@*" data-semver="3.3.2" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<script data-require="jquery@2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-touch.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-animate.js"></script>
<script data-require="ui-bootstrap@*" data-semver="0.12.1" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.12.1.min.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/csv.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/pdfmake.js"></script>
<script src="http://ui-grid.info/docs/grunt-scripts/vfs_fonts.js"></script>
<script src="/dist/release/ui-grid.js"></script>
<link rel="stylesheet" href="/dist/release/ui-grid.css" type="text/css" />
<style type="text/css">
.grid {
width: 100%;
max-width: 600px;
height: 400px;
}

body {
padding: 20px;
}
</style>
</head>

<body>
<div ng-controller="MainCtrl">
<button ng-click="openModal()" class="btn btn-success">Open Modal</button>
</div>

<script>
var app = angular.module('app', ['ui.grid', 'ui.bootstrap']);

app.controller('MainCtrl', function ($scope, $modal) {
$scope.openModal = function () {
$modal.open({
templateUrl: 'modal.html'
});
}
});

app.controller('ModalDemoCtrl', function ($scope, $http) {
$scope.gridOptions = {};

$http.get('https://cdn.rawgit.com/angular-ui/ui-grid.info/gh-pages/data/500_complex.json')
.success(function(data) {
$scope.gridOptions.data = data;
});
});
</script>

<script type="text/ng-template" id="modal.html">
<div ng-controller="ModalDemoCtrl">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<div id="grid1" ui-grid="gridOptions" class="grid"></div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="$close()">Cancel</button>
</div>
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
</div>
</script>
</body>
</html>
226 changes: 125 additions & 101 deletions src/js/core/directives/ui-grid.js
Expand Up @@ -160,124 +160,148 @@
</file>
</example>
*/
angular.module('ui.grid').directive('uiGrid',
[
'$compile',
'$templateCache',
'gridUtil',
'$window',
'uiGridConstants',
function(
$compile,
$templateCache,
gridUtil,
$window,
uiGridConstants
) {
angular.module('ui.grid').directive('uiGrid', uiGridDirective);

uiGridDirective.$inject = ['$compile', '$templateCache', '$timeout', '$window', 'gridUtil', 'uiGridConstants'];
function uiGridDirective($compile, $templateCache, $timeout, $window, gridUtil, uiGridConstants) {
return {
templateUrl: 'ui-grid/ui-grid',
scope: {
uiGrid: '='
},
replace: true,
transclude: true,
controller: 'uiGridController',
compile: function () {
return {
templateUrl: 'ui-grid/ui-grid',
scope: {
uiGrid: '='
},
replace: true,
transclude: true,
controller: 'uiGridController',
compile: function () {
return {
post: function ($scope, $elm, $attrs, uiGridCtrl) {
// gridUtil.logDebug('ui-grid postlink');

var grid = uiGridCtrl.grid;

// Initialize scrollbars (TODO: move to controller??)
uiGridCtrl.scrollbars = [];

//todo: assume it is ok to communicate that rendering is complete??
grid.renderingComplete();

grid.element = $elm;

grid.gridWidth = $scope.gridWidth = gridUtil.elementWidth($elm);

// Default canvasWidth to the grid width, in case we don't get any column definitions to calculate it from
grid.canvasWidth = uiGridCtrl.grid.gridWidth;

grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);

// If the grid isn't tall enough to fit a single row, it's kind of useless. Resize it to fit a minimum number of rows
if (grid.gridHeight < grid.options.rowHeight && grid.options.enableMinHeightCheck) {
// Figure out the new height
var contentHeight = grid.options.minRowsToShow * grid.options.rowHeight;
var headerHeight = grid.options.showHeader ? grid.options.headerRowHeight : 0;
var footerHeight = grid.calcFooterHeight();

var scrollbarHeight = 0;
if (grid.options.enableHorizontalScrollbar === uiGridConstants.scrollbars.ALWAYS) {
scrollbarHeight = gridUtil.getScrollbarWidth();
}
post: function ($scope, $elm, $attrs, uiGridCtrl) {
var grid = uiGridCtrl.grid;
// Initialize scrollbars (TODO: move to controller??)
uiGridCtrl.scrollbars = [];
grid.element = $elm;

var maxNumberOfFilters = 0;
// Calculates the maximum number of filters in the columns
angular.forEach(grid.options.columnDefs, function(col) {
if (col.hasOwnProperty('filter')) {
if (maxNumberOfFilters < 1) {
maxNumberOfFilters = 1;
}
}
else if (col.hasOwnProperty('filters')) {
if (maxNumberOfFilters < col.filters.length) {
maxNumberOfFilters = col.filters.length;
}
}
});
var filterHeight = maxNumberOfFilters * headerHeight;

var newHeight = headerHeight + contentHeight + footerHeight + scrollbarHeight + filterHeight;
// See if the grid has a rendered width, if not, wait a bit and try again
var sizeCheckInterval = 100; // ms
var maxSizeChecks = 20; // 2 seconds total
var sizeChecks = 0;

$elm.css('height', newHeight + 'px');
// Setup (event listeners) the grid
setup();

grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);
}
// And initialize it
init();

// Run initial canvas refresh
grid.refreshCanvas();
// Mark rendering complete so API events can happen
grid.renderingComplete();

//if we add a left container after render, we need to watch and react
$scope.$watch(function () { return grid.hasLeftContainer();}, function (newValue, oldValue) {
if (newValue === oldValue) {
return;
}
grid.refreshCanvas(true);
});
// If the grid doesn't have size currently, wait for a bit to see if it gets size
checkSize();

//if we add a right container after render, we need to watch and react
$scope.$watch(function () { return grid.hasRightContainer();}, function (newValue, oldValue) {
if (newValue === oldValue) {
return;
}
grid.refreshCanvas(true);
});
/*-- Methods --*/

function checkSize() {
// If the grid has no width and we haven't checked more than <maxSizeChecks> times, check again in <sizeCheckInterval> milliseconds
if ($elm[0].offsetWidth <= 0 && sizeChecks < maxSizeChecks) {
setTimeout(checkSize, sizeCheckInterval);
sizeChecks++;
}
else {
$timeout(init);
}
}

// Resize the grid on window resize events
function gridResize($event) {
grid.gridWidth = $scope.gridWidth = gridUtil.elementWidth($elm);
grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);
// Setup event listeners and watchers
function setup() {
// Bind to window resize events
angular.element($window).on('resize', gridResize);

grid.refreshCanvas(true);
// Unbind from window resize events when the grid is destroyed
$elm.on('$destroy', function () {
angular.element($window).off('resize', gridResize);
});

// If we add a left container after render, we need to watch and react
$scope.$watch(function () { return grid.hasLeftContainer();}, function (newValue, oldValue) {
if (newValue === oldValue) {
return;
}
grid.refreshCanvas(true);
});

angular.element($window).on('resize', gridResize);
// If we add a right container after render, we need to watch and react
$scope.$watch(function () { return grid.hasRightContainer();}, function (newValue, oldValue) {
if (newValue === oldValue) {
return;
}
grid.refreshCanvas(true);
});
}

// Unbind from window resize events when the grid is destroyed
$elm.on('$destroy', function () {
angular.element($window).off('resize', gridResize);
});
// Initialize the directive
function init() {
grid.gridWidth = $scope.gridWidth = gridUtil.elementWidth($elm);

// Default canvasWidth to the grid width, in case we don't get any column definitions to calculate it from
grid.canvasWidth = uiGridCtrl.grid.gridWidth;

grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);

// If the grid isn't tall enough to fit a single row, it's kind of useless. Resize it to fit a minimum number of rows
if (grid.gridHeight < grid.options.rowHeight && grid.options.enableMinHeightCheck) {
autoAdjustHeight();
}

// Run initial canvas refresh
grid.refreshCanvas(true);
}

// Set the grid's height ourselves in the case that its height would be unusably small
function autoAdjustHeight() {
// Figure out the new height
var contentHeight = grid.options.minRowsToShow * grid.options.rowHeight;
var headerHeight = grid.options.showHeader ? grid.options.headerRowHeight : 0;
var footerHeight = grid.calcFooterHeight();

var scrollbarHeight = 0;
if (grid.options.enableHorizontalScrollbar === uiGridConstants.scrollbars.ALWAYS) {
scrollbarHeight = gridUtil.getScrollbarWidth();
}
};

var maxNumberOfFilters = 0;
// Calculates the maximum number of filters in the columns
angular.forEach(grid.options.columnDefs, function(col) {
if (col.hasOwnProperty('filter')) {
if (maxNumberOfFilters < 1) {
maxNumberOfFilters = 1;
}
}
else if (col.hasOwnProperty('filters')) {
if (maxNumberOfFilters < col.filters.length) {
maxNumberOfFilters = col.filters.length;
}
}
});
var filterHeight = maxNumberOfFilters * headerHeight;

var newHeight = headerHeight + contentHeight + footerHeight + scrollbarHeight + filterHeight;

$elm.css('height', newHeight + 'px');

grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);
}

// Resize the grid on window resize events
function gridResize($event) {
grid.gridWidth = $scope.gridWidth = gridUtil.elementWidth($elm);
grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);

grid.refreshCanvas(true);
}
}
};
}
]);
};
}

})();
2 changes: 1 addition & 1 deletion src/js/core/directives/ui-pinned-container.js
Expand Up @@ -33,7 +33,7 @@
}

return width;
}
}
}

function updateContainerDimensions() {
Expand Down
8 changes: 5 additions & 3 deletions src/js/core/services/ui-grid-util.js
Expand Up @@ -106,7 +106,7 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
function getWidthOrHeight( elem, name, extra ) {
// Start with offset property, which is equivalent to the border-box value
var valueIsBorderBox = true,
val,
val, // = name === 'width' ? elem.offsetWidth : elem.offsetHeight,
styles = getStyles(elem),
isBorderBox = styles['boxSizing'] === 'border-box';

Expand Down Expand Up @@ -173,6 +173,8 @@ module.service('gridUtil', ['$log', '$window', '$document', '$http', '$templateC
function ($log, $window, $document, $http, $templateCache, $timeout, $injector, $q, $interpolate, uiGridConstants) {
var s = {

augmentWidthOrHeight: augmentWidthOrHeight,

getStyles: getStyles,

/**
Expand Down Expand Up @@ -786,8 +788,8 @@ module.service('gridUtil', ['$log', '$window', '$document', '$http', '$templateC
if (e) {
var styles = getStyles(e);
return e.offsetWidth === 0 && rdisplayswap.test(styles.display) ?
s.fakeElement(e, cssShow, function(newElm) {
return getWidthOrHeight( newElm, name, extra );
s.swap(e, cssShow, function() {
return getWidthOrHeight(e, name, extra );
}) :
getWidthOrHeight( e, name, extra );
}
Expand Down
2 changes: 1 addition & 1 deletion src/less/header.less
Expand Up @@ -7,7 +7,7 @@

.ui-grid-header {
border-bottom: 1px solid @borderColor;
box-sizing: content-box;
box-sizing: border-box;
}

.ui-grid-top-panel {
Expand Down
2 changes: 1 addition & 1 deletion src/less/menu.less
Expand Up @@ -28,7 +28,7 @@
overflow: hidden;
padding: 0 10px 20px 10px;
cursor: pointer;
box-sizing: content-box;
box-sizing: border-box;
}

.ui-grid-menu .ui-grid-menu-inner {
Expand Down
3 changes: 2 additions & 1 deletion test/unit/core/services/ui-grid-util.spec.js
Expand Up @@ -199,7 +199,8 @@ describe('ui.grid.utilService', function() {
expect(w).toEqual(300);
});

it('should work with hidden element', function() {
// Width is no longer calculated for hidden elements
xit('should work with hidden element', function() {
angular.element(elm).remove();

elm = document.createElement('div');
Expand Down

0 comments on commit e7dfb8c

Please sign in to comment.