Full row editing is for when you want all cells in the row to become editable at the same time. This gives the impression to the user that the record the row represents is being edited.
To enable full row editing, set the grid option editType = 'fullRow'.
If using custom cell editors, the cell editors will work in the exact same way with the following additions:
focusIn: If your cell editor has afocusIn()method, it will get called when the user tabs into the cell. This should be used to put the focus on the particular item to be focused, e.g. thetextfieldwithin your cell editor.focusOut: If your cell editor has afocusOut()method, it will get called when the user tabs out of the cell. There is no intended use for this; it's just there to complement thefocusIn()method.- Events: When a row stops editing, the
cellValueChangedevent gets called for each column whose value has changed, androwValueChangedgets called once for the row.
Full Row Edit and Popup Editors Copy Link
Full row editing is not compatible with popup editors. This is because a) the grid would look confusing to pop up an editor for each cell in the row at the same time and b) the complexity of navigation and popup is almost impossible to model, so the grid and your application code would be messy and very error prone. If you are using full row edit, then you are prevented from using popup editors.
This does not mean that you cannot show a popup from your 'in cell' editor - you are free to do that - however the responsibility of showing and hiding the popup belongs with your editor. You may want to use the grid's focus events to hide the popups when the user tabs or clicks out of the cell.
Example: Full Row Edit Copy Link
The example below shows full row editing. In addition to standard full row editing, the following should also be noted:
- The 'Price' column has a custom editor demonstrating how you should implement the
focusIn()method. BothfocusIn()andfocusOut()for this editor are logged to the console. Note thatfocusIn()andfocusOut()are only called when the user is tabbing between cells when editing, they are not called as the user double clicks on a cell to start editing that cell, or the user finishes editing that cell by e.g. hitting the ↵ Enter key. - Pressing ⇥ Tab / ⇧ Shift & ⇥ Tab while editing will move the focus between the cells on the editing row. Read only cells will be focusable while the row is in edit mode.
- The 'Suppress Navigable' column is not navigable using ⇥ Tab / ⇧ Shift & ⇥ Tab. In other words, when tabbing around the grid, you cannot tab onto this cell.
- The Read Only column is not editable, so when the row goes into edit mode, the cell in the Read Only column cannot be edited.
- The button will start editing line two. It uses the API to start editing a cell, however the result is that the whole row will become editable starting with the specified cell.
cellValueChangedandrowValueChangedevents are logged to console.
'use client';
import React, {
StrictMode,
useCallback,
useMemo,
useRef,
useState,
} from "react";
import { createRoot } from "react-dom/client";
import type {
CellValueChangedEvent,
ColDef,
RowValueChangedEvent,
} from "ag-grid-community";
import {
ClientSideRowModelModule,
CustomEditorModule,
SelectEditorModule,
TextEditorModule,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ColumnsToolPanelModule,
ContextMenuModule,
} from "ag-grid-enterprise";
import { AgGridProvider, AgGridReact } from "ag-grid-react";
import NumericCellEditor from "./numericCellEditor";
import "./styles.css";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
const modules = [
SelectEditorModule,
ClientSideRowModelModule,
ColumnsToolPanelModule,
ColumnMenuModule,
ContextMenuModule,
CustomEditorModule,
TextEditorModule,
];
function getRowData(): any[] {
const rowData: any[] = [];
for (let i = 0; i < 10; i++) {
rowData.push({
make: "Toyota",
model: "Celica",
price: 35000 + i * 1000,
field4: "Sample XX",
field5: "Sample 22",
field6: "Sample 23",
});
rowData.push({
make: "Ford",
model: "Mondeo",
price: 32000 + i * 1000,
field4: "Sample YY",
field5: "Sample 24",
field6: "Sample 25",
});
rowData.push({
make: "Porsche",
model: "Boxster",
price: 72000 + i * 1000,
field4: "Sample ZZ",
field5: "Sample 26",
field6: "Sample 27",
});
}
return rowData;
}
const GridExample = () => {
const gridRef = useRef<AgGridReact>(null);
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [rowData, setRowData] = useState<any[]>(getRowData());
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{
field: "make",
cellEditor: "agSelectCellEditor",
cellEditorParams: {
values: ["Porsche", "Toyota", "Ford", "AAA", "BBB", "CCC"],
},
},
{ field: "model" },
{ field: "field4", headerName: "Read Only", editable: false },
{ field: "price", cellEditor: NumericCellEditor },
{
headerName: "Suppress Navigable",
field: "field5",
suppressNavigable: true,
minWidth: 200,
},
{ headerName: "Read Only", field: "field6", editable: false },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
editable: true,
cellDataType: false,
};
}, []);
const onCellValueChanged = useCallback((event: CellValueChangedEvent) => {
console.log(
"onCellValueChanged: " + event.colDef.field + " = " + event.newValue,
);
}, []);
const onRowValueChanged = useCallback((event: RowValueChangedEvent) => {
const data = event.data;
console.log(
"onRowValueChanged: (" +
data.make +
", " +
data.model +
", " +
data.price +
", " +
data.field5 +
")",
);
}, []);
const onBtStopEditing = useCallback(() => {
gridRef.current!.api.stopEditing();
}, []);
const onBtStartEditing = useCallback(() => {
gridRef.current!.api.setFocusedCell(1, "make");
gridRef.current!.api.startEditingCell({
rowIndex: 1,
colKey: "make",
});
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div className="example-wrapper">
<div style={{ marginBottom: "5px" }}>
<button style={{ fontSize: "12px" }} onClick={onBtStartEditing}>
Start Editing Line 2
</button>
<button style={{ fontSize: "12px" }} onClick={onBtStopEditing}>
Stop Editing
</button>
</div>
<div style={gridStyle}>
<AgGridReact
ref={gridRef}
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
editType={"fullRow"}
onCellValueChanged={onCellValueChanged}
onRowValueChanged={onRowValueChanged}
/>
</div>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
import React, { memo, useCallback, useEffect, useRef } from "react";
import type { CustomCellEditorProps } from "ag-grid-react";
import { useGridCellEditor } from "ag-grid-react";
export default memo(
({
value,
onValueChange,
eventKey,
cellStartedEdit,
}: CustomCellEditorProps) => {
const refInput = useRef<HTMLInputElement>(null);
const updateValue = (val: string) => {
onValueChange(val === "" ? null : parseInt(val));
};
useEffect(() => {
updateValue(isCharNumeric(eventKey) ? eventKey : value);
// we only want to highlight this cell if it started the edit; it's possible
// another cell in this row started the edit
if (cellStartedEdit) {
refInput.current?.focus();
refInput.current?.select();
}
}, []);
const isCharNumeric = (charStr: string | null) => {
return charStr != null && !!/^\d+$/.test(charStr);
};
const isNumericKey = (event: any) => {
const charStr = event.key;
return isCharNumeric(charStr);
};
const onKeyDown = (event: any) => {
if (!event.key || event.key.length !== 1 || isNumericKey(event)) {
return;
}
refInput.current?.focus();
if (event.preventDefault) event.preventDefault();
};
// when we tab into this editor, we want to focus the contents
const focusIn = useCallback(() => {
refInput.current?.focus();
refInput.current?.select();
console.log("NumericCellEditor.focusIn()");
}, []);
// when we tab out of the editor, this gets called
const focusOut = useCallback(() => {
console.log("NumericCellEditor.focusOut()");
}, []);
useGridCellEditor({
focusIn,
focusOut,
});
return (
<input
ref={refInput}
value={value == null ? "" : value}
onChange={(event: any) => updateValue(event.target.value)}
onKeyDown={(event: any) => onKeyDown(event)}
className="ag-input-field-input"
/>
);
},
);