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 {
createApp,
defineComponent,
onBeforeMount,
ref,
shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
ColDef,
ColGroupDef,
GridApi,
GridOptions,
GridReadyEvent,
HighlightChangesModule,
IServerSideDatasource,
ModuleRegistry,
RowApiModule,
RowModelType,
enableDevValidations,
} from "ag-grid-community";
import {
RowGroupingModule,
ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
ModuleRegistry.registerModules([
RowApiModule,
HighlightChangesModule,
RowGroupingModule,
ServerSideRowModelModule,
]);
let versionCounter: number = 0;
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 VueExample = defineComponent({
template: `
<div style="height: 100%">
<div class="example-wrapper">
<div style="margin-bottom: 5px">
<button v-on:click="setRows()">Set Rows</button>
<button v-on:click="updateRows()">Update Rows</button>
</div>
<ag-grid-vue
style="width: 100%; height: 100%;"
@grid-ready="onGridReady"
:columnDefs="columnDefs"
:defaultColDef="defaultColDef"
:rowModelType="rowModelType"
:cacheBlockSize="cacheBlockSize"
:rowData="rowData"></ag-grid-vue>
</div>
</div>
`,
components: {
"ag-grid-vue": AgGridVue,
},
setup(props) {
const gridApi = shallowRef<GridApi | null>(null);
const columnDefs = ref<ColDef[]>([
{ field: "athlete" },
{ field: "date" },
{ field: "country" },
{ field: "version" },
]);
const defaultColDef = ref<ColDef>({
flex: 1,
sortable: false,
enableCellChangeFlash: true,
});
const rowModelType = ref<RowModelType>("serverSide");
const cacheBlockSize = ref(75);
const rowData = ref<any[]>(null);
function setRows() {
versionCounter += 1;
const version =
versionCounter + " - " + versionCounter + " - " + versionCounter;
gridApi.value!.forEachNode((node) => {
node.setData({ ...node.data, version });
});
}
function updateRows() {
versionCounter += 1;
const version =
versionCounter + " - " + versionCounter + " - " + versionCounter;
gridApi.value!.forEachNode((node) => {
node.updateData({ ...node.data, version });
});
}
const onGridReady = (params: GridReadyEvent) => {
gridApi.value = params.api;
const updateData = (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
params.api!.setGridOption("serverSideDatasource", datasource);
};
fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
.then((resp) => resp.json())
.then((data) => updateData(data));
};
return {
gridApi,
columnDefs,
defaultColDef,
rowModelType,
cacheBlockSize,
rowData,
onGridReady,
setRows,
updateRows,
};
},
});
const app = createApp(VueExample);
app.mount("#app");
.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;
}
}
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):
this.gridApi.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 {
createApp,
defineComponent,
onBeforeMount,
ref,
shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
ColDef,
ColGroupDef,
GetRowIdFunc,
GridApi,
GridOptions,
GridReadyEvent,
HighlightChangesModule,
IServerSideDatasource,
ModuleRegistry,
RowApiModule,
RowModelType,
enableDevValidations,
} from "ag-grid-community";
import {
RowGroupingModule,
ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
ModuleRegistry.registerModules([
RowApiModule,
HighlightChangesModule,
RowGroupingModule,
ServerSideRowModelModule,
]);
let versionCounter: number = 0;
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 VueExample = defineComponent({
template: `
<div style="height: 100%">
<div class="example-wrapper">
<div style="margin-bottom: 5px">
<button v-on:click="updateRows('Michael Phelps')">Update All Michael Phelps Records</button>
<button v-on:click="updateRows('Michael Phelps', '29/08/2004')">Update Michael Phelps, 29/08/2004</button>
<button v-on:click="updateRows('Aleksey Nemov', '01/10/2000')">Update Aleksey Nemov, 01/10/2000</button>
<button v-on:click="updateRows(undefined, '12/08/2012')">Update All Records Dated 12/08/2012</button>
</div>
<ag-grid-vue
style="width: 100%; height: 100%;"
@grid-ready="onGridReady"
:columnDefs="columnDefs"
:defaultColDef="defaultColDef"
:rowModelType="rowModelType"
:cacheBlockSize="cacheBlockSize"
:getRowId="getRowId"
:rowData="rowData"></ag-grid-vue>
</div>
</div>
`,
components: {
"ag-grid-vue": AgGridVue,
},
setup(props) {
const gridApi = shallowRef<GridApi | null>(null);
const columnDefs = ref<ColDef[]>([
{ field: "athlete" },
{ field: "date" },
{ field: "country" },
{ field: "version" },
]);
const defaultColDef = ref<ColDef>({
flex: 1,
sortable: false,
enableCellChangeFlash: true,
});
const rowModelType = ref<RowModelType>("serverSide");
const cacheBlockSize = ref(75);
const getRowId = ref<GetRowIdFunc>(
(params) => `${params.data.athlete}-${params.data.date}`,
);
const rowData = ref<any[]>(null);
function updateRows(athlete?: string, date?: string) {
versionCounter += 1;
gridApi.value!.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 onGridReady = (params: GridReadyEvent) => {
gridApi.value = params.api;
const updateData = (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
params.api!.setGridOption("serverSideDatasource", datasource);
};
fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
.then((resp) => resp.json())
.then((data) => updateData(data));
};
return {
gridApi,
columnDefs,
defaultColDef,
rowModelType,
cacheBlockSize,
getRowId,
rowData,
onGridReady,
updateRows,
};
},
});
const app = createApp(VueExample);
app.mount("#app");
.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;
}
}
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 {
createApp,
defineComponent,
onBeforeMount,
ref,
shallowRef,
} from "vue";
import { AgGridVue } from "ag-grid-vue3";
import "./styles.css";
import {
ColDef,
ColGroupDef,
GetRowIdFunc,
GridApi,
GridOptions,
GridReadyEvent,
HighlightChangesModule,
IServerSideDatasource,
ModuleRegistry,
RowModelType,
RowSelectionOptions,
enableDevValidations,
} from "ag-grid-community";
import {
RowGroupingModule,
ServerSideRowModelModule,
} from "ag-grid-enterprise";
import { FakeServer } from "./fakeServer";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
ModuleRegistry.registerModules([
HighlightChangesModule,
RowGroupingModule,
ServerSideRowModelModule,
]);
let versionCounter: number = 0;
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 VueExample = defineComponent({
template: `
<div style="height: 100%">
<div class="example-wrapper">
<div style="margin-bottom: 5px">
<button v-on:click="updateSelectedRows()">Update Selected Rows</button>
</div>
<ag-grid-vue
style="width: 100%; height: 100%;"
@grid-ready="onGridReady"
:columnDefs="columnDefs"
:defaultColDef="defaultColDef"
:rowSelection="rowSelection"
:rowModelType="rowModelType"
:cacheBlockSize="cacheBlockSize"
:getRowId="getRowId"
:rowData="rowData"></ag-grid-vue>
</div>
</div>
`,
components: {
"ag-grid-vue": AgGridVue,
},
setup(props) {
const gridApi = shallowRef<GridApi | null>(null);
const columnDefs = ref<ColDef[]>([
{ field: "athlete" },
{ field: "country" },
{ field: "date" },
{ field: "version" },
]);
const defaultColDef = ref<ColDef>({
flex: 1,
sortable: false,
enableCellChangeFlash: true,
});
const rowSelection = ref<RowSelectionOptions | "single" | "multiple">({
mode: "multiRow",
headerCheckbox: false,
});
const rowModelType = ref<RowModelType>("serverSide");
const cacheBlockSize = ref(75);
const getRowId = ref<GetRowIdFunc>(
(params) => `${params.data.athlete}-${params.data.date}`,
);
const rowData = ref<any[]>(null);
function updateSelectedRows() {
versionCounter += 1;
const version =
versionCounter + " - " + versionCounter + " - " + versionCounter;
const nodesToUpdate = gridApi.value!.getSelectedNodes();
nodesToUpdate.forEach((node) => {
node.updateData({ ...node.data, version });
});
}
const onGridReady = (params: GridReadyEvent) => {
gridApi.value = params.api;
const updateData = (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
params.api!.setGridOption("serverSideDatasource", datasource);
};
fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
.then((resp) => resp.json())
.then((data) => updateData(data));
};
return {
gridApi,
columnDefs,
defaultColDef,
rowSelection,
rowModelType,
cacheBlockSize,
getRowId,
rowData,
onGridReady,
updateSelectedRows,
};
},
});
const app = createApp(VueExample);
app.mount("#app");
.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;
}
}