web-dev-qa-db-ja.com

Angular ui-gridはグリッドの高さを動的に計算します

私は使用しています: 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程度までしか広がりません。残っているスペースは空白なので、25行ではなく8行しか表示されません。グリッドには400pxの空き領域がありますが、下にスクロールする必要があります。 ui-grid-viewportとui-grid-canvasは両方ともこのスペースを使用していません...

Ui-grid-viewportがそのスペースを使用できないのはなぜですか?

44

コンテナの高さいっぱいになると スクロールの問題が修正される になるので、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>
76
tony

より単純なアプローチは、cssおよびminRowsToShow値を動的に設定することと組み合わせてvirtualizationThresholdを使用するように設定されます。

スタイルシートで:

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

コードでは、datagridOptionsを変更するたびに以下の関数を呼び出します。 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 ;
}
18
LeOn - Han Li

UPDATE

HTMLが要求されたため、以下に貼り付けました。

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

オリジナル

画面の面積に基づいて高さと幅を設定するレスポンシブCSS(@media)を使用することで、この問題を適切に解決することができました。以下のようなもの(そして明らかに、ニーズに基づいて追加できます):

@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;
  }
}

このソリューションの最大の利点は、グリッドサイズの変更を監視するためにサイズ変更イベント処理が必要ないことです。それはただ動作します。

3
icfantv

.ui-grid、.ui-grid-viewport、.ui-grid-contents-wrapper、.ui-grid-canvas {height:auto!important; }

1
Slava

私はトニーのアプローチが好きです。動作しますが、別の方法で実装することにしました。ここに私のコメント:

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 }
        ]
    }
1
Aquiles

私はゲームに遅れていますが、ニースの解決策を見つけました。 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>
1
Jamie Steele

@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";
}

グリッドには、ウィンドウの高さに関しても動的な高さがあります。

0
cmTanko

トニーのアプローチは私にとってはうまくいきますが、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>
0
dephiros