This section demonstrates updating rows directly while using the Server-Side Row Model (SSRM).
Updating Rows API Copy Link
You can update a single row by using the row node updateData or setData functions.
Updates the data on the rowNode. When this method is called, the grid refreshes the entire rendered row if it is displayed. |
Replaces the data on the rowNode. When this method is called, the grid refreshes the entire rendered row if it is displayed. |
Setting row data will NOT change the row node ID, so if you are using getRowId() and the data changes such that the ID will be different, the rowNode will not have its ID updated.
Updating Rows Example Copy Link
The example below demonstrates a basic example, using the API's forEachNode function to iterate over all loaded nodes, and updating their version.
Set Data: Sets the row data using
setDataand the grid refreshes the row, notably the cells won't flash withenableCellChangeFlash.Update Data: Updates the row data using
updateDataand the grid refreshes the row, notably the cells do flash withenableCellChangeFlash.
import {
ColDef,
GridApi,
GridOptions,
HighlightChangesModule,
IServerSideDatasource,
ModuleRegistry,
RowApiModule,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
RowGroupingModule,
ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
RowApiModule,
HighlightChangesModule,
RowGroupingModule,
ServerSideRowModelModule,
]);
let versionCounter: number = 0;
const columnDefs: ColDef[] = [
{ field: "athlete" },
{ field: "date" },
{ field: "country" },
{ field: "version" },
];
let gridApi: GridApi;
const gridOptions: GridOptions = {
defaultColDef: {
flex: 1,
sortable: false,
enableCellChangeFlash: true,
},
columnDefs: columnDefs,
// use the enterprise row model
rowModelType: "serverSide",
cacheBlockSize: 75,
};
function setRows() {
versionCounter += 1;
const version =
versionCounter + " - " + versionCounter + " - " + versionCounter;
gridApi!.forEachNode((node) => {
node.setData({ ...node.data, version });
});
}
function updateRows() {
versionCounter += 1;
const version =
versionCounter + " - " + versionCounter + " - " + versionCounter;
gridApi!.forEachNode((node) => {
node.updateData({ ...node.data, version });
});
}
const getServerSideDatasource = (server: any): IServerSideDatasource => {
return {
getRows: (params) => {
console.log("[Datasource] - rows requested by grid: ", params.request);
const response = server.getData(params.request);
const dataWithVersion = response.rows.map((rowData: any) => {
return {
...rowData,
version:
versionCounter + " - " + versionCounter + " - " + versionCounter,
};
});
// adding delay to simulate real server call
setTimeout(() => {
if (response.success) {
// call the success callback
params.success({
rowData: dataWithVersion,
rowCount: response.lastRow,
});
} else {
// inform the grid request failed
params.fail();
}
}, 1000);
},
};
};
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);
});
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).setRows = setRows;
(<any>window).updateRows = updateRows;
}
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
// 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 class="example-wrapper">
<div style="margin-bottom: 5px">
<button onClick="setRows()">Set Rows</button>
<button onClick="updateRows()">Update Rows</button>
</div>
<div id="myGrid"></div>
</div>
Specific Row Updates Copy Link
The following code snippet outlines the general approach of iterating through all loaded row nodes and then updating target rows with rowNode.updateData(data):
api.forEachNode(rowNode => {
if (idsToUpdate.indexOf(rowNode.data.id) >= 0) {
// arbitrarily update some data
const updated = rowNode.data;
updated.gold += 1;
// directly update data in rowNode
rowNode.updateData(updated);
}
});The example below demonstrates this snippet in action;
import {
ColDef,
GridApi,
GridOptions,
HighlightChangesModule,
IServerSideDatasource,
ModuleRegistry,
RowApiModule,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
RowGroupingModule,
ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
RowApiModule,
HighlightChangesModule,
RowGroupingModule,
ServerSideRowModelModule,
]);
let versionCounter: number = 0;
const columnDefs: ColDef[] = [
{ field: "athlete" },
{ field: "date" },
{ field: "country" },
{ field: "version" },
];
let gridApi: GridApi;
const gridOptions: GridOptions = {
defaultColDef: {
flex: 1,
sortable: false,
enableCellChangeFlash: true,
},
columnDefs,
// use the enterprise row model
rowModelType: "serverSide",
cacheBlockSize: 75,
getRowId: (params) => `${params.data.athlete}-${params.data.date}`,
};
function updateRows(athlete?: string, date?: string) {
versionCounter += 1;
gridApi!.forEachNode((rowNode) => {
if (athlete != null && rowNode.data?.athlete !== athlete) {
// if the athlete doesn't match, skip this row
// Or row data is empty as it could be the loading row
return;
}
if (date != null && rowNode.data?.date !== date) {
return;
}
// arbitrarily update some data
const updated = rowNode.data;
updated.version =
versionCounter + " - " + versionCounter + " - " + versionCounter;
// directly update data in rowNode
rowNode.updateData(updated);
});
}
const getServerSideDatasource = (server: any): IServerSideDatasource => {
return {
getRows: (params) => {
console.log("[Datasource] - rows requested by grid: ", params.request);
const response = server.getData(params.request);
const dataWithVersion = response.rows.map((rowData: any) => {
return {
...rowData,
version:
versionCounter + " - " + versionCounter + " - " + versionCounter,
};
});
// adding delay to simulate real server call
setTimeout(() => {
if (response.success) {
// call the success callback
params.success({
rowData: dataWithVersion,
rowCount: response.lastRow,
});
} else {
// inform the grid request failed
params.fail();
}
}, 1000);
},
};
};
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);
});
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).updateRows = updateRows;
}
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
// 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 class="example-wrapper">
<div style="margin-bottom: 5px">
<button onClick="updateRows('Michael Phelps')">Update All Michael Phelps Records</button>
<button onClick="updateRows('Michael Phelps', '29/08/2004')">Update Michael Phelps, 29/08/2004</button>
<button onClick="updateRows('Aleksey Nemov', '01/10/2000')">Update Aleksey Nemov, 01/10/2000</button>
<button onClick="updateRows(undefined, '12/08/2012')">Update All Records Dated 12/08/2012</button>
</div>
<div id="myGrid"></div>
</div>
Selected Row Updates Copy Link
The example below demonstrates how to update all of the rows which the user has selected, note the following:
- The Update Selected Rows button will update the row version directly on the selected row nodes.
- The selected nodes are obtained using
api.getSelectedNodes(), and are then individually updated.
import {
ColDef,
GridApi,
GridOptions,
HighlightChangesModule,
IServerSideDatasource,
ModuleRegistry,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
RowGroupingModule,
ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
HighlightChangesModule,
RowGroupingModule,
ServerSideRowModelModule,
]);
let versionCounter: number = 0;
const columnDefs: ColDef[] = [
{ field: "athlete" },
{ field: "country" },
{ field: "date" },
{ field: "version" },
];
let gridApi: GridApi;
const gridOptions: GridOptions = {
defaultColDef: {
flex: 1,
sortable: false,
enableCellChangeFlash: true,
},
columnDefs: columnDefs,
rowSelection: { mode: "multiRow", headerCheckbox: false },
// use the enterprise row model
rowModelType: "serverSide",
cacheBlockSize: 75,
getRowId: (params) => `${params.data.athlete}-${params.data.date}`,
};
const getServerSideDatasource = (server: any): IServerSideDatasource => {
return {
getRows: (params) => {
console.log("[Datasource] - rows requested by grid: ", params.request);
const response = server.getData(params.request);
const dataWithVersion = response.rows.map((rowData: any) => {
return {
...rowData,
version:
versionCounter + " - " + versionCounter + " - " + versionCounter,
};
});
// adding delay to simulate real server call
setTimeout(() => {
if (response.success) {
// call the success callback
params.success({
rowData: dataWithVersion,
rowCount: response.lastRow,
});
} else {
// inform the grid request failed
params.fail();
}
}, 1000);
},
};
};
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);
});
function updateSelectedRows() {
versionCounter += 1;
const version =
versionCounter + " - " + versionCounter + " - " + versionCounter;
const nodesToUpdate = gridApi!.getSelectedNodes();
nodesToUpdate.forEach((node) => {
node.updateData({ ...node.data, version });
});
}
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).updateSelectedRows = updateSelectedRows;
}
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
// 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 class="example-wrapper">
<div style="margin-bottom: 5px">
<button onClick="updateSelectedRows()">Update Selected Rows</button>
</div>
<div id="myGrid"></div>
</div>