Hi
I'm using this example from the docs for my project: https://www.telerik.com/kendo-react-ui/components/grid/editing/editing-in-cell/
1) What is the editField in state used for in this example as it doesn't appear to affect anything
2) I've only got one cell in the grid that I want to edit. While setting editable={false} on the other columns stops them from being editable it doesn't stop them from getting the onClick enterEdit, I thought I could be able to do my own custom filtering here but it appears the editable prop isn't passed through.
3) How would you recommend sending data to the server after a user has updated the cell, I don't want to use a save changes button. I would like the data to be sent after the user has removed focus from the input so exitEdit seems perfect, but it doesn't fire unless you click off the row, if you click on another cell nothing happens.
4) I have a default value for this cell that says required if it's blank. On clicking the cell it clears the value so the input is empty. If the user doesn't add anything it should add the required value back on so it can be seen in the grid again. It works when I click away from the row but clicking on another cell the value stays blank.
I would be great if you could help me with these questions.
Thanks
13 Answers, 1 is accepted
cellRender = (tdElement, cellProps) => {
const dataItem = cellProps.dataItem;
const cellField = cellProps.field;
const inEditField = dataItem[
this
.editFieldName];
let additionalProps =
null
;
if
(cellField && cellField === inEditField) {
additionalProps = {
ref: (td) => {
const input = td && td.querySelector(
"input"
);
const activeElement = document.activeElement;
if
(
!input ||
!activeElement ||
input === activeElement ||
!activeElement.contains(input)
) {
return
;
}
if
(input.type ===
"checkbox"
) {
input.focus();
}
else
{
input.select();
}
input.addEventListener(
"keypress"
, (e) => {
if
(e.key ===
"Enter"
) {
this
.exitEdit(e, dataItem, cellField);
}
});
},
};
}
else
{
if
(cellField ===
"MyField"
) {
additionalProps = {
onClick: (e) => {
this
.enterEdit(e, dataItem, cellField);
},
};
}
else
{
additionalProps = {
onClick: (e) => {
this
.exitEdit(e, dataItem, cellField);
},
};
}
}
Hello, Sam,
Regarding the questions:
1) The editField is used in order to know which field from the row is clicked and to place only that cell from the row in edit mode.
2) The enterEdit called on the onClick event is programmatically set. We can suggest performing a check if the click field is the same as the one that can be edited and only then call the enterEdit method. As for the editable props, we can log an enhancement request to pass the prop down. The main reason why it is not set now is that we presume that inside the custom cell render, the developer knows which fields can be edit and which not.
3) This is because the onBlur handler in the demo is attached to the rowRender. The same approach can be done to attach it to the cellRender and then it will be fired after the user leaves the cell.
4 new) To clarify the issue is that on the keypress handler the dataItem that is received is the old one. If this is correct, we can see the value of e.targer which should hold the latest one:
input.addEventListener("keypress", (e) => {
if (e.key === "Enter") {
this.exitEdit(e, dataItem, cellField);
}
});
Regards,
Stefan
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.
Thanks for your reply I fixed 4) using your suggestion.
I'm having trouble with input lag while I'm updating my cell when I have a lot of rows in my grid (above 10).
const onItemChange = (event) => {
const gridStateCopy = cloneDeep(gridState);
const data = gridStateCopy.data.map((item) => {
return
item.WeeklyTimesheetsId === event.dataItem.WeeklyTimesheetsId
? { ...item, [event.field]: event.value }
: item;
});
gridStateCopy.data = data;
setGridState(gridStateCopy);
};
This is my itemChange function and it updates the whole grid every time you enter a new value, is there a better way of doing this?
Hello, Sam,
I`m glad to hear that some of the issues are resolved.
As for the input lag, the Grid should be updating instantly when typing. I extended our demo to have 60 items and there was no input lag observed:
https://stackblitz.com/edit/react-9shc2l?file=app/sample-products.jsx
The code on the onItemChange event looks good, I`m only not completely sure on how fast the cloneDeep method is. Is it possible to check if removing it will make a difference?
Also, when testing for performance, please use the production build as React is times faster when building for production:
https://reactjs.org/docs/optimizing-performance.html
Regards,
Stefan
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.
We have 16 columns in our grid and I think that's what causes the issue. I'm not sure how to copy the gridState correctly without clonedeep.
https://stackblitz.com/edit/react-9shc2l?file=app%2Fmain.jsx
Hello, Sam,
I added 15 columns to the example and it was still updating as expected. Also, we have a columnVirtualization feature, which can be used in Grid with many columns:
https://www.telerik.com/kendo-react-ui/components/grid/columns/column-virtualization/
As for clone deep, I only wanted to remove it for the test, to see if it is causing a performance issue. Only if it is, we will need to replace it.
If it is possible please reproduce the input lag in the example as this will help to locally test the reason for it.
Regards,
Stefan
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.
Hi, is this problem solved? I'm having a similar input lag issue using this example https://www.telerik.com/kendo-react-ui/components/grid/editing/#toc-editing-options in our application. So I didn't have these rowRender or cellRender code from the in-cell editing example. Do I need these renderers to solve the input lag?
Hello, Di,
Is it possible to share an example or at least a code sample to observe the setup?
Usually, the Grid should have no input lag, unless we are performing a resource-consuming operation each time the user types.
The most common places to check are the cellRender, the render of the component that has the Grid, and the onItemChange event.
Regards,
Stefan
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.
Hi Stefan,
Here is a code example taking directly from our app. It's an inline editing so we don't have rowRender. We have onItemChange but I didn't see anything out of the ordinary. This is the kendo example we followed in this code example: https://www.telerik.com/kendo-react-ui/components/grid/editing/#toc-editing-options.
Thank you,
Di
Code Example:
/* eslint-disable no-param-reassign */
/* eslint-disable react/destructuring-assignment */
/* eslint-disable react/no-array-index-key */
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Grid, GridColumn } from '@progress/kendo-react-grid';
import {
getPortfolioOptions, loadPortfolios, updatePortfolioOptions, editPortfolioOptions,
} from '../actions/allocatorActions';
import MyCommandCell from './MyCommandCell';
import DropDownCell from './DropDownCell';
const PortfoliosOptions = () => {
const [editID, setEditID] = useState(null);
const [original, setOriginal] = useState({});
const dispatch = useDispatch();
useEffect(() => {
dispatch(getPortfolioOptions());
}, []);
const {
portsOptions, portOptions,
} = useSelector(state => ({
portsOptions: state.allocator.portsOptions,
portOptions: state.allocator.portOptions,
}));
const rowClick = (event) => {
setEditID(event.dataItem.portfolio_name);
};
const itemChange = (event) => {
const inEditID = event.dataItem.portfolio_name;
let data = [];
let updated = {};
const [target] = portsOptions.filter(item => item.portfolio_name === inEditID);
if (event.field === 'rating_required') {
updated = { ...target, [event.field]: event.value.value };
data = portsOptions.map(item => (item.portfolio_name === inEditID ? { ...item, [event.field]: event.value.value } : item));
} else {
updated = { ...target, [event.field]: event.value };
data = portsOptions.map(item => (item.portfolio_name === inEditID ? { ...item, [event.field]: event.value } : item));
}
dispatch(loadPortfolios(data));
dispatch(updatePortfolioOptions(updated));
setOriginal(target);
};
const update = () => {
dispatch(editPortfolioOptions(portOptions));
setEditID(null);
};
const cancel = () => {
dispatch(getPortfolioOptions('Canceling...'));
dispatch(updatePortfolioOptions(original));
setEditID(null);
};
const CommandCell = MyCommandCell({
update, cancel,
});
return (
<div>
{portsOptions != null && (
<Grid
style={{ width: '1450px', height: '400px' }}
columnVirtualization
editField="inEdit"
data={portsOptions != null ? portsOptions.map(item => ({ ...item, inEdit: item.portfolio_name === editID })) : null}
onRowClick={rowClick}
onItemChange={itemChange}
>
<GridColumn field="portfolio_name" title="Portfolio" editable={false} width="80px" headerClassName="grid-header" />
<GridColumn field="tactical" title="Tactical" editor="boolean" width="50px" headerClassName="grid-header" />
<GridColumn field="preferred" title="Preferred" editor="boolean" width="50px" headerClassName="grid-header" />
<GridColumn field="special_cash" title="Special Cash" editor="boolean" width="50px" headerClassName="grid-header" />
<GridColumn field="rating_required" title="Rating Required" cell={DropDownCell} width="100px" headerClassName="grid-header" />
<GridColumn field="notes" title="Notes" editor="text" width="930px" headerClassName="grid-header" />
<GridColumn cell={CommandCell} width="160px" headerClassName="grid-header" />
</Grid>
)}
</div>
);
};
export default PortfoliosOptions;
Hello, Di,
Thank you for the code.
Based on the provided code I assume that maybe the dispatch on every itemChange could be causing this depending on how fast the store operations are.
I can suggest keeping the updated data inside the component state (useState Hook) and only updating the store when the user clicks update in order to not send actions to the store each time the user types.
Regards,
Stefan
Progress Telerik
Our thoughts here at Progress are with those affected by the outbreak.