Learn how to set Row Height when using the Server-Side Row Model.
Dynamic Row Height Copy Link
To enable Dynamic Row Height when using the Server-Side Row Model you need to provide an implementation for the getRowHeight Grid Options property. This is demonstrated in the example below:
Callback version of property rowHeight to set height for each row individually. Function should return a positive number of pixels, or return null/undefined to use the default row height. |
import {
GridApi,
GridOptions,
IServerSideDatasource,
ModuleRegistry,
RowHeightParams,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
RowGroupingModule,
ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
import { IOlympicData } from "./interfaces";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([RowGroupingModule, ServerSideRowModelModule]);
let gridApi: GridApi<IOlympicData>;
const gridOptions: GridOptions<IOlympicData> = {
columnDefs: [
{ field: "country", rowGroup: true, hide: true },
{ field: "year", rowGroup: true, hide: true },
{ field: "gold", aggFunc: "sum" },
{ field: "silver", aggFunc: "sum" },
{ field: "bronze", aggFunc: "sum" },
],
defaultColDef: {
flex: 1,
minWidth: 100,
},
autoGroupColumnDef: {
flex: 1,
minWidth: 180,
},
// use the server-side row model
rowModelType: "serverSide",
// dynamically set row heights
getRowHeight: (params: RowHeightParams) => {
if (params.node.level === 0) {
return 80;
}
if (params.node.level === 1) {
return 60;
}
return 40;
},
suppressAggFuncInHeader: true,
};
function getServerSideDatasource(server: any): IServerSideDatasource {
return {
getRows: (params) => {
console.log("[Datasource] - rows requested by grid: ", params.request);
const response = server.getData(params.request);
// adding delay to simulate real server call
setTimeout(() => {
if (response.success) {
// call the success callback
params.success({
rowData: response.rows,
rowCount: response.lastRow,
});
} else {
// inform the grid request failed
params.fail();
}
}, 200);
},
};
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
.then((response) => response.json())
.then(function (data) {
// setup the fake server with entire dataset
const fakeServer = new FakeServer(data);
// create datasource with a reference to the fake server
const datasource = getServerSideDatasource(fakeServer);
// register the datasource with the grid
gridApi!.setGridOption("serverSideDatasource", datasource);
});
// This fake server uses http://alasql.org/ to mimic how a real server
// might generate sql queries from the Server-Side Row Model request.
// To keep things simple it does the bare minimum to support the example.
export function FakeServer(allData) {
alasql.options.cache = false;
return {
getData: function (request) {
const results = executeQuery(request);
return {
success: true,
rows: results,
lastRow: getLastRowIndex(request),
};
},
};
function executeQuery(request) {
const sql = buildSql(request);
console.log('[FakeServer] - about to execute query:', sql);
return alasql(sql, [allData]);
}
function buildSql(request) {
return (
selectSql(request) +
' FROM ?' +
whereSql(request) +
groupBySql(request) +
orderBySql(request) +
limitSql(request)
);
}
function selectSql(request) {
const rowGroupCols = request.rowGroupCols;
const valueCols = request.valueCols;
const groupKeys = request.groupKeys;
if (isDoingGrouping(rowGroupCols, groupKeys)) {
const rowGroupCol = rowGroupCols[groupKeys.length];
const colsToSelect = [rowGroupCol.id];
valueCols.forEach(function (valueCol) {
colsToSelect.push(valueCol.aggFunc + '(' + valueCol.id + ') AS ' + valueCol.id);
});
return 'SELECT ' + colsToSelect.join(', ');
}
return 'SELECT *';
}
function whereSql(request) {
const rowGroups = request.rowGroupCols;
const groupKeys = request.groupKeys;
const whereParts = [];
if (groupKeys) {
groupKeys.forEach(function (key, i) {
const value = typeof key === 'string' ? "'" + key + "'" : key;
whereParts.push(rowGroups[i].id + ' = ' + value);
});
}
if (whereParts.length > 0) {
return ' WHERE ' + whereParts.join(' AND ');
}
return '';
}
function groupBySql(request) {
const rowGroupCols = request.rowGroupCols;
const groupKeys = request.groupKeys;
if (isDoingGrouping(rowGroupCols, groupKeys)) {
const rowGroupCol = rowGroupCols[groupKeys.length];
return ' GROUP BY ' + rowGroupCol.id + ' HAVING count(*) > 0';
}
return '';
}
function orderBySql(request) {
const sortModel = request.sortModel;
if (sortModel.length === 0) return '';
const sorts = sortModel.map(function (s) {
return s.colId + ' ' + s.sort.toUpperCase();
});
return ' ORDER BY ' + sorts.join(', ');
}
function limitSql(request) {
if (request.endRow == undefined || request.startRow == undefined) {
return '';
}
const blockSize = request.endRow - request.startRow;
return ' LIMIT ' + blockSize + ' OFFSET ' + request.startRow;
}
function isDoingGrouping(rowGroupCols, groupKeys) {
// we are not doing grouping if at the lowest level
return rowGroupCols.length > groupKeys.length;
}
function getLastRowIndex(request) {
return executeQuery({ ...request, startRow: undefined, endRow: undefined }).length;
}
}
<div id="myGrid" style="height: 100%"></div>
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} Ensure maxBlocksInCache is not set when using dynamic row height.
Auto Row Height Copy Link
To have the grid calculate the row height based on the cell contents, set autoHeight=true on columns that require variable height. The grid will calculate the height once when the data is loaded into the grid.
In the example below, Column A & B have autoHeight=true and wrapText=true. See Row Height for details on these properties.
import {
ColDef,
GridApi,
GridOptions,
IServerSideDatasource,
ModuleRegistry,
RowAutoHeightModule,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
RowGroupingModule,
ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { getData } from "./data";
import { FakeServer } from "./fakeServer";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
RowAutoHeightModule,
RowGroupingModule,
ServerSideRowModelModule,
]);
const columnDefs: ColDef[] = [
{
headerName: "Group",
field: "name",
rowGroup: true,
hide: true,
},
{
field: "autoA",
wrapText: true,
autoHeight: true,
aggFunc: "last",
},
{
field: "autoB",
wrapText: true,
autoHeight: true,
aggFunc: "last",
},
];
let gridApi: GridApi;
const gridOptions: GridOptions = {
columnDefs: columnDefs,
defaultColDef: {
flex: 1,
},
autoGroupColumnDef: {
flex: 1,
maxWidth: 200,
},
// use the server-side row model
rowModelType: "serverSide",
suppressAggFuncInHeader: true,
onGridReady: (params) => {
// generate data for example
const data = getData();
// setup the fake server with entire dataset
const fakeServer = new FakeServer(data);
// create datasource with a reference to the fake server
const datasource = getServerSideDatasource(fakeServer);
// register the datasource with the grid
params.api.setGridOption("serverSideDatasource", datasource);
},
};
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
function getServerSideDatasource(server: any): IServerSideDatasource {
return {
getRows: (params) => {
console.log("[Datasource] - rows requested by grid: ", params.request);
const response = server.getData(params.request);
// adding delay to simulate real server call
setTimeout(() => {
if (response.success) {
// call the success callback
params.success({
rowData: response.rows,
rowCount: response.lastRow,
});
} else {
// inform the grid request failed
params.fail();
}
}, 200);
},
};
}
export function getData(): any[] {
const latinSentence =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit';
function generateRandomSentence() {
return latinSentence.slice(0, Math.floor(window.agRandom() * 100)) + '.';
}
const rowData = [];
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 50; j++) {
rowData.push({
name: 'Group ' + j,
autoA: generateRandomSentence(),
autoB: generateRandomSentence(),
});
}
}
return rowData;
}
// This fake server uses http://alasql.org/ to mimic how a real server
// might generate sql queries from the Server-Side Row Model request.
// To keep things simple it does the bare minimum to support the example.
export function FakeServer(allData) {
alasql.options.cache = false;
return {
getData: function (request) {
const results = executeQuery(request);
return {
success: true,
rows: results,
lastRow: getLastRowIndex(request),
};
},
};
function executeQuery(request) {
const sql = buildSql(request);
console.log('[FakeServer] - about to execute query:', sql);
return alasql(sql, [allData]);
}
function buildSql(request) {
return (
selectSql(request) +
' FROM ?' +
whereSql(request) +
groupBySql(request) +
orderBySql(request) +
limitSql(request)
);
}
function selectSql(request) {
const rowGroupCols = request.rowGroupCols;
const valueCols = request.valueCols;
const groupKeys = request.groupKeys;
if (isDoingGrouping(rowGroupCols, groupKeys)) {
const rowGroupCol = rowGroupCols[groupKeys.length];
const colsToSelect = [rowGroupCol.id];
valueCols.forEach(function (valueCol) {
colsToSelect.push(valueCol.aggFunc + '(' + valueCol.id + ') AS ' + valueCol.id);
});
return 'SELECT ' + colsToSelect.join(', ');
}
return 'SELECT *';
}
function whereSql(request) {
const rowGroups = request.rowGroupCols;
const groupKeys = request.groupKeys;
const whereParts = [];
if (groupKeys) {
groupKeys.forEach(function (key, i) {
const value = typeof key === 'string' ? "'" + key + "'" : key;
whereParts.push(rowGroups[i].id + ' = ' + value);
});
}
if (whereParts.length > 0) {
return ' WHERE ' + whereParts.join(' AND ');
}
return '';
}
function groupBySql(request) {
const rowGroupCols = request.rowGroupCols;
const groupKeys = request.groupKeys;
if (isDoingGrouping(rowGroupCols, groupKeys)) {
const rowGroupCol = rowGroupCols[groupKeys.length];
return ' GROUP BY ' + rowGroupCol.id + ' HAVING count(*) > 0';
}
return '';
}
function orderBySql(request) {
const sortModel = request.sortModel;
if (sortModel.length === 0) return '';
const sorts = sortModel.map(function (s) {
return s.colId + ' ' + s.sort.toUpperCase();
});
return ' ORDER BY ' + sorts.join(', ');
}
function limitSql(request) {
if (request.endRow == undefined || request.startRow == undefined) {
return '';
}
const blockSize = request.endRow - request.startRow;
return ' LIMIT ' + blockSize + ' OFFSET ' + request.startRow;
}
function isDoingGrouping(rowGroupCols, groupKeys) {
// we are not doing grouping if at the lowest level
return rowGroupCols.length > groupKeys.length;
}
function getLastRowIndex(request) {
return executeQuery({ ...request, startRow: undefined, endRow: undefined }).length;
}
}
<div id="myGrid" style="height: 100%"></div>
Ensure maxBlocksInCache is not set when using auto row height.
Changing Row Height Copy Link
To dynamically set or restore row heights in the Server-Side Row Model, use setRowHeight() to apply custom heights to specific rows and resetRowHeights() to revert all rows to the values calculated by the getRowHeight() in Grid Options. See Changing Row Height for more.
Tells the grid to recalculate the row heights. |
The following example demonstrates this functionality:
- Clicking on a row sets its height to
100pxusingsetRowHeight(). - Clicking the "Reset Row Heights" button resets all rows to their original heights using
resetRowHeights().
import { GridApi, GridOptions } from "ag-grid-community";
import {
ModuleRegistry,
ServerSideRowModelApiModule,
ServerSideRowModelModule,
createGrid,
enableDevValidations,
} from "ag-grid-enterprise";
import { IOlympicDataWithId } from "./interfaces";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
ServerSideRowModelModule,
ServerSideRowModelApiModule,
]);
let gridApi: GridApi<IOlympicDataWithId>;
const gridOptions: GridOptions<IOlympicDataWithId> = {
columnDefs: [
{ field: "athlete", minWidth: 200 },
{ field: "age" },
{ field: "country", minWidth: 180 },
{ field: "year" },
{ field: "date", minWidth: 150 },
{ field: "gold" },
{ field: "silver" },
{ field: "bronze" },
{ field: "total" },
],
defaultColDef: {
flex: 1,
minWidth: 100,
// allow every column to be aggregated
enableValue: true,
sortable: false,
},
getRowId: (p) => String(p.data?.id),
getRowHeight: (p) => {
return 50 + 30 * Math.sin((p.data?.id ?? 0) / 5 - Math.PI / 2);
},
autoGroupColumnDef: {
minWidth: 200,
},
onRowClicked: (p) => {
p.node.setRowHeight(100);
p.api.onRowHeightChanged();
},
// use the server-side row model
rowModelType: "serverSide",
};
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
function resetRowHeights() {
gridApi.resetRowHeights();
}
gridApi = createGrid(gridDiv, gridOptions);
function createServerSideDatasource(server) {
return {
getRows: (params) => {
console.log("[Datasource] - rows requested by grid: ", params.request);
// get data for request from our fake server
const response = server.getData(params.request);
// simulating real server call with a 500ms delay
setTimeout(() => {
if (response.success) {
// supply rows for requested block to grid
params.success({
rowData: response.rows,
rowCount: response.lastRow,
});
} else {
params.fail();
}
}, 500);
},
};
}
function createFakeServer(allData) {
return {
getData: (request) => {
// take a slice of the total rows for requested block
const rowsForBlock = allData.slice(request.startRow, request.endRow);
// here we are pretending we don't know the last row until we reach it!
const lastRow = getLastRowIndex(request, rowsForBlock);
return {
success: true,
rows: rowsForBlock,
lastRow: lastRow,
};
},
};
}
function getLastRowIndex(request, results) {
if (!results) return undefined;
const currentLastRow = (request.startRow || 0) + results.length;
// if on or after the last block, work out the last row, otherwise return 'undefined'
return currentLastRow < (request.endRow || 0) ? currentLastRow : undefined;
}
fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
.then((response) => response.json())
.then(function (data) {
// adding row id to data
let idSequence = 0;
data.forEach(function (item: { id: number }) {
item.id = idSequence++;
});
// setup the fake server with entire dataset
const fakeServer = createFakeServer(data);
// create datasource with a reference to the fake server
const datasource = createServerSideDatasource(fakeServer);
// register the datasource with the grid
gridApi.setGridOption("serverSideDatasource", datasource);
});
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).resetRowHeights = resetRowHeights;
}
<div style="height: 100%">
<button onclick="resetRowHeights()">Reset Row Heights</button>
<div id="myGrid" style="height: 90%"></div>
</div>
export interface IOlympicDataWithId extends IOlympicData {
id: number;
}
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
}