Angular ui-grid 动态计算表格高度

44

我正在使用:https://github.com/angular-ui/ui-grid.info/tree/gh-pages/release/3.0.0-RC.18

<div ui-grid="gridOptions" style="height:765px"></div>

当我像上面展示的那样硬编码数值时,网格会展开并且一切都按预期工作。

然而,如果我做以下操作……

$scope.gridStyle = 'height:'+numRows*rowHeight+'px' //(765px);
<div ui-grid="gridOptions" style="{{gridStyle}}"></div>
高度在div中打印,div变宽了,但内容本身只扩展到大约340px。剩下的空间是空白的,所以我只能看到8行而不是25行。我必须向下滚动,而网格中有整个400px的空闲空间。ui-grid-viewport和ui-grid-canvas都没有使用这个空间......
为什么ui-grid-viewport不能使用那个空间呢?

据我的理解,网格不提供动态高度。但是,您可以将ui-grid-auto-resize附加到您的网格上,并尝试调整数据以适应它。这就是我解决问题的方法(+为您的网格提供通过函数计算高度的方法,我使用ng-class =“myStyle()”,其中myStyle只是我行高和$scope.gridOptions.data.length之间的乘积)。希望能有所帮助! - Jose
8个回答

77

我使用 ui-grid - v3.0.0-rc.20,因为当你达到容器的完整高度时,已解决了滚动问题。使用 ui.grid.autoResize 模块可以动态自适应调整网格以适应您的数据。使用以下函数计算您的网格高度。使用 ui-if 是可选的,可以在渲染之前等待数据设置。

angular.module('app',['ui.grid','ui.grid.autoResize']).controller('AppController', ['uiGridConstants', function(uiGridConstants) {
    ...
    
    $scope.gridData = {
      rowHeight: 30, // set row height, this is default size
      ...
    };
  
    ...

    $scope.getTableHeight = function() {
       var rowHeight = 30; // your row height
       var headerHeight = 30; // your header height
       return {
          height: ($scope.gridData.data.length * rowHeight + headerHeight) + "px"
       };
    };
      
    ...
<div ui-if="gridData.data.length>0" id="grid1" ui-grid="gridData" class="grid" ui-grid-auto-resize ng-style="getTableHeight()"></div>


请注意,使用此方法可能会有性能问题,因为它设置一个计时器,每250毫秒执行一次以查看网格大小是否已更改。如果您不想走这条路,请参阅我的有关响应式CSS的答案。 - icfantv
1
假设我有一个按钮,当点击它时会添加一个新的ui-grid行,有没有办法避免250毫秒的定时器执行? - neelmeg
1
可接受的、可行的解决方案。应该在ui-grid手册/教程中提供。 - Augustas
1
如果行高不是恒定的,是否有解决方案?提问者假设行高是恒定的,因此我在这里开了一个新问题:http://stackoverflow.com/questions/40311664/angular-ui-grid-auto-height-if-rows-have-non-constant-height - user64141
在@tony的回答中,函数$scope.getTableHeight将返回一个对象,我们需要一个字符串,所以return 'height:' + ($scope.gridData.data.length * rowHeight + headerHeight) + 'px';很好用。而且ng-style对我不起作用,我可以在html中使用style="{{getTableHeight()}}" - Belter
有人可以看一下这个问题吗?https://stackoverflow.com/questions/45072428/ui-grid-custom-directive-height-not-getting-updated-when-i-change-the-data - Syed Rasheed

20

更简单的方法是使用css结合动态设置minRowsToShowvirtualizationThreshold值。

在样式表中:

.ui-grid, .ui-grid-viewport {
    height: auto !important;
}

在代码中,每次更改gridOptions中的data时,请调用以下函数。 maxRowToShow是您预定义的值,对于我的用例,我将其设置为25。

ES5:

setMinRowsToShow(){
    //if data length is smaller, we shrink. otherwise we can do pagination.
    $scope.gridOptions.minRowsToShow = Math.min($scope.gridOptions.data.length, $scope.maxRowToShow);
    $scope.gridOptions.virtualizationThreshold = $scope.gridOptions.minRowsToShow ;
}

考虑到这个修复的简单性,它的效果非常好 - 但有一个缺陷:如果您需要可扩展的网格,则会破坏嵌套网格的滚动。 - Treeline
我一直在努力更新页面大小变化时的高度。我使用了你的CSS,并将代码放在setMinRowsToShow()中,在gridApi.pagination.on.paginationChanged内部,这对我非常有效。 - Paul
当我改变数据长度时,我的问题出现了。https://stackoverflow.com/questions/45072428/ui-grid-custom-directive-height-not-getting-updated-when-i-change-the-data - Syed Rasheed
这是一个很好的解决方案,因为它不涉及绝对高度。 - mystarrocks

4

.ui-grid、.ui-grid-viewport、.ui-grid-contents-wrapper、.ui-grid-canvas { 高度:自动重要! }

该段文字涉及到IT技术中的CSS样式表,意为将指定类名的元素高度设为自动。

3

更新:

HTML 被请求,因此我已将其粘贴在下方。

<div ui-grid="gridOptions" class="my-grid"></div>

翻译:

我们能够通过使用响应式CSS(@media)来解决这个问题,该CSS根据屏幕空间设置高度和宽度。类似于以下内容(当然您可以根据需要添加更多):

@media (min-width: 1024px) {
  .my-grid {
    width: 772px;
  }
}

@media (min-width: 1280px) {
  .my-grid {
    width: 972px;
  }
}

@media (min-height: 768px) {
  .my-grid {
    height: 480px;
  }
}

@media (min-height: 900px) {
  .my-grid {
    height: 615px;
  }
}

这个解决方案最好的部分是我们不需要调整大小事件处理来监视网格大小的变化。它只是有效地工作。

1
你的类设置被覆盖了,是不是“style”应用到了你的网格上? - vratojr
我不直接应用任何样式到我们的网格中,只添加一个类属性。我将更新上面的示例。 - icfantv
1
我明白,但事实是ui.grid会自行设置样式,特别是它会设置高度属性...也许我们使用的是不同版本或者不同的条件.. :/ - vratojr
是的,我不知道。我只知道我们的代码可以正常工作,而且我不需要使用 !important - icfantv

1

我喜欢Tony的方法。它很有效,但我决定采用不同的方式实现。以下是我的评论:

1)我进行了一些测试,当使用ng-style时,Angular会评估ng-style内容,也就是说会多次调用getTableHeight()函数。我在getTableHeight()函数中设置了断点来分析这个问题。

顺便说一下,ui-if已被移除。现在你可以使用ng-if内置指令。

2)我更喜欢写一个类似这样的服务:

angular.module('angularStart.services').factory('uiGridService', function ($http, $rootScope) {

var factory = {};

factory.getGridHeight = function(gridOptions) {

    var length = gridOptions.data.length;
    var rowHeight = 30; // your row height
    var headerHeight = 40; // your header height
    var filterHeight = 40; // your filter height

    return length * rowHeight + headerHeight + filterHeight + "px";
}
factory.removeUnit = function(value, unit) {

    return value.replace(unit, '');
}
return factory;

然后在控制器中编写以下内容:

  angular.module('app',['ui.grid']).controller('AppController', ['uiGridConstants', function(uiGridConstants) {

  ...

  // Execute this when you have $scope.gridData loaded...
  $scope.gridHeight = uiGridService.getGridHeight($scope.gridData);

在HTML文件中:
  <div id="grid1" ui-grid="gridData" class="grid" ui-grid-auto-resize style="height: {{gridHeight}}"></div>

当Angular应用样式时,它只需要查看$scope.gridHeight变量,而不必评估完整的函数。
3)如果您想动态计算可扩展网格的高度,则更加复杂。在这种情况下,您可以设置expandableRowHeight属性。这将为每个子网格固定保留高度。
    $scope.gridData = {
        enableSorting: true,
        multiSelect: false,  
        enableRowSelection: true,
        showFooter: false,
        enableFiltering: true,    
        enableSelectAll: false,
        enableRowHeaderSelection: false, 
        enableGridMenu: true,
        noUnselect: true,
        expandableRowTemplate: 'subGrid.html',
        expandableRowHeight: 380,   // 10 rows * 30px + 40px (header) + 40px (filters)
        onRegisterApi: function(gridApi) {

            gridApi.expandable.on.rowExpandedStateChanged($scope, function(row){
                var height = parseInt(uiGridService.removeUnit($scope.jdeNewUserConflictsGridHeight,'px'));
                var changedRowHeight = parseInt(uiGridService.getGridHeight(row.entity.subGridNewUserConflictsGrid, true));

                if (row.isExpanded)
                {
                    height += changedRowHeight;                    
                }
                else
                {
                    height -= changedRowHeight;                    
                }

                $scope.jdeNewUserConflictsGridHeight = height + 'px';
            });
        },
        columnDefs :  [
                { field: 'GridField1', name: 'GridField1', enableFiltering: true }
        ]
    }

gridOptions.data.length 返回的是字符串的长度,而不是数组的长度,在我的情况下 "referenceData.Subscriptions" = 21 个字符。如果将数据属性定义为字符串值,例如: $scope.gridOptions = { enableColumnResizing: true, data: 'referenceData.Subscriptions', - Damian Green

1

托尼的方法对我有效,但当执行console.log时,函数getTableHeight被调用了太多次(排序、菜单点击...)

我进行了修改,所以只有在添加/删除行时才重新计算高度。注意:tableData是行数组。

$scope.getTableHeight = function() {
   var rowHeight = 30; // your row height
   var headerHeight = 30; // your header height
   return {
      height: ($scope.gridData.data.length * rowHeight + headerHeight) + "px"
   };
};

$scope.$watchCollection('tableData', function (newValue, oldValue) {
    angular.element(element[0].querySelector('.grid')).css($scope.getTableHeight());
});

Html

<div id="grid1" ui-grid="gridData" class="grid" ui-grid-auto-resize"></div>

1
我来晚了,但是我找到了一个不错的解决方案。我创建了一个自定义属性指令,只需传入 gridApi 参数,它就会自动计算高度。它还订阅了分页更改事件,因此如果用户更改页面大小,它也会重新调整大小。
class UIGridAutoResize implements ng.IDirective {
    link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => void;
    scope: { gridApi: "=" };
    restrict = "A";

    private previousValue: string;
    private isValid: boolean = true;
    private watch: any;

    constructor($timeout: ng.ITimeoutService) {
        UIGridAutoResize.prototype.link = (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
            const gridOptions = scope.$eval(attrs.uiGrid) as any;
            const gridApi = scope.$eval(attrs.gridResize) as any;

            gridApi.core.on.rowsRendered(scope, () => {
                $timeout(() => {
                    this.autoSizeGrid(element, attrs, gridOptions, gridApi, false);
                }, 100);
            });

            gridApi.core.on.filterChanged(scope, () => {
                this.autoSizeGrid(element, attrs, gridOptions, gridApi, false);
            });

            if (attrs.uiGridPagination === "") {
                gridApi.pagination.on.paginationChanged(null, () => {
                    this.autoSizeGrid(element, attrs, gridOptions, gridApi, true);
                });
            }

            angular.element(window).resize(() => {
                $timeout(() => {
                    this.autoSizeGrid(element, attrs, gridOptions, gridApi, false);
                }, 100);
            });
        };
    }

    static Factory(): ng.IDirectiveFactory {
        const directive = ($timeout: ng.ITimeoutService) => {
            return new UIGridAutoResize($timeout);
        };

        directive["$inject"] = ["$timeout"];

        return directive;
    }

    private autoSizeGrid(element: ng.IAugmentedJQuery, attrs: ng.IAttributes, gridOptions: any, gridApi: any, isPaginationChanged: boolean) {
        gridApi.core.handleWindowResize();

        // Clear empty grid message 
        angular.element(element.parent()).find("#emptyGridMessage").remove();
        element.find(".ui-grid-viewport").css("display", "");

        if (attrs.hidePageSize === "") {
            element.find(".ui-grid-pager-row-count-picker").css("display", "none");
        }

        let rowCount = gridApi.core.getVisibleRows().length;

        const headerElements = element.find(".ui-grid-header");
        let headerHeight = 2;

        if (headerElements.length > 1) { // If we have more than one header element the grid is using grouping
            const headerElement = angular.element(headerElements[1]);
            headerHeight += headerElement.height();
        } else {
            headerHeight += headerElements.height();
        }

        if (attrs.uiGridPagination === "") {
            if (rowCount < 1) {
                gridOptions.enablePagination = false;
                gridOptions.enablePaginationControls = false;
                element.css("height", (rowCount * 30) + headerHeight - 2);
                element.find(".ui-grid-viewport").css("display", "none");
                angular.element("<div id='emptyGridMessage' style='font-size: 1em; width: 100%; background-color: white; border: 1px solid #d4d4d4; padding: 7px 12px; color: #707070;'><span style='opacity: 0.95;'>There are no records.</span></div>").insertAfter(element);
            } else if (gridApi.core.getVisibleRows().length < gridOptions.paginationPageSize && !isPaginationChanged) {
                gridOptions.enablePagination = false;
                gridOptions.enablePaginationControls = false;
                element.css("height", (rowCount * 30) + headerHeight);
            } else {
                gridOptions.enablePagination = true;
                gridOptions.enablePaginationControls = true;              
                element.css("height", (rowCount * 30) + headerHeight);
            }
        } else {
            if (rowCount < 1) {
                element.css("height", (rowCount * 30) + headerHeight - 2);
                element.find(".ui-grid-viewport").css("display", "none");
                angular.element("<div id='emptyGridMessage' style='font-size: 1em; width: 100%; background-color: white; border: 1px solid #d4d4d4; padding: 7px 12px; color: #707070;'><span style='opacity: 0.95;'>There are no records.</span></div>").insertAfter(element);
            } else {
                element.css("height", (rowCount * 30) + headerHeight);
            }
        }

        // Add extra margin to prevent scroll bar and pager from overlapping content underneath
        const pagerHeight = element.find(".ui-grid-pager-panel").height();

        if (rowCount > 0) {
            if (pagerHeight > 0)
                element.css("margin-bottom", pagerHeight);
            else
                element.css("margin-bottom", 10);
        } else {
            if (pagerHeight > 0)
                angular.element(element.parent()).find("#emptyGridMessage").css("margin-bottom", pagerHeight);
            else 
                angular.element(element.parent()).find("#emptyGridMessage").css("margin-bottom", 10);
        }

        if (rowCount > gridOptions.paginationPageSize) // Sometimes paging shows all rows this fixes that
           gridApi.core.refresh();
    }
}

<div ui-grid="vm.gridOptions" grid-resize="vm.gridApi" ui-grid-resize-columns ui-grid-pagination></div>

0

跟随 @tony 的方法,将 getTableHeight() 函数改为

<div id="grid1" ui-grid="$ctrl.gridOptions" class="grid" ui-grid-auto-resize style="{{$ctrl.getTableHeight()}}"></div>

getTableHeight() {
    var offsetValue = 365;
    return "height: " + parseInt(window.innerHeight - offsetValue ) + "px!important";
}

网格的高度将根据窗口高度而动态调整。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接