Grid inCell editing Blur event block button click

2 Answers 90 Views
Grid
Mikael
Top achievements
Rank 1
Iron
Mikael asked on 23 Oct 2024, 08:44 PM

Hi,

I used the grid incell editing exemple ( https://www.telerik.com/kendo-react-ui/components/grid/editing/editing-in-cell/ ) to do modification in a list. The only thing I add to the exemple was the onBlur in the rowRender, because the exitEdit was not called anywhere.

Now I have a problem, because I have a delete button at the end of my row and if I click on it when i'm in edit the onClick event of my button is not called only the onBlur is called and I can't figure out what i'm doing wrong.

here is my custom row/cell render that I modified (I put the modification in bold)

import * as React from "react";
export const CellRender = (props) => {
    const dataItem = props.originalProps.dataItem;
    const cellField = props.originalProps.field;
    const inEditField = dataItem[props.editField || ""];
    const additionalProps =
        cellField && cellField === inEditField
            ? {
                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();
                    }
                },
            }
            : {
                onClick: () => {
                    props.enterEdit(dataItem, cellField);
                },
            };
    const clonedProps = {
        ...props.td.props,
        ...additionalProps,
    };
    return React.cloneElement(props.td, clonedProps, props.td.props.children);
};
export const RowRender = (props) => {
    const trProps = {
        ...props.tr.props,
        ...{
            onBlur: (e) => {
                    props.exitEdit()
            }
        }
    };
    return React.cloneElement(
        props.tr,
        {
            ...trProps,
        },
        props.tr.props.children
    );
};


and here is the code for the components

const removeCell = (props) => {
    const { dataItem } = props;
    return (
        <td className="k-command-cell">
            <Button onClick={() => delete(dataItem)}><span className="k-icon k-font-icon k-i-close-outline k-icon-xl redIcon"></span></Button>
        </td>
    );
}

const customCellRender = (td, props) => (
    <CellRender
        originalProps={props}
        td={td}
        enterEdit={(dataItem, field) => setState(enterEdit(list, dataItem, field))}
        editField={EDIT_FIELD}
    />
);
const customRowRender = (tr, props) => (
    <RowRender
        originalProps={props}
        tr={tr}
        exitEdit={() => setState(exitEdit())}
        editField={EDIT_FIELD}
    />
);

const enterEdit = (list, dataItem, field) => {
    return list.map((item) => ({
        ...item,
        [EDIT_FIELD]: item.id=== dataItem.id? field : null,
    }));
};

const exitEdit = () => {
    return list.map((item) => ({
        ...item,
        [EDIT_FIELD]: null,
    }));
};

And here is my grid

<Grid
    data={list}
    dataItemKey={"id"}
    total={list.length}
    cellRender={customCellRender}
    rowRender={customRowRender}
    onItemChange={itemChange}
    editField={EDIT_FIELD}
>
    <GridToolbar>
        <Button onClick={AddNewItem}><span className="k-icon k-font-icon k-i-plus-circle k-icon-xl greenIcon"></span> Add</Button>
    </GridToolbar>
    <Column field="Name" title="Name" editable={true} />
    <Column title="Delete" cells={{ data: removeCell, }} width="100px"></Column>
</Grid>

Thanks you for your help.

2 Answers, 1 is accepted

Sort by
0
Accepted
Konstantin Dikov
Telerik team
answered on 24 Oct 2024, 08:51 AM

Hi Mikael,

Thank you for reaching out to us.

The issue that you are facing is due to the fact that you have defined the delete custom cell within the main component (as inline function), which will force the Grid to re-mount and re-render on each change in the state. This means that when you blur a cell, the entire Grid will re-mount and the button that was clicked will no longer exist (it will be replaced with new button). To resolve this you will have to define the custom cell as a separate component outside of the main component and pass the delete function reference using React Context. Here is an example demonstrating this approach:

Please have in mind that this applies to all custom cells/components used within the Grid and they should not be defined inline.

Hope this helps.

 

Regards,
Konstantin Dikov
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

0
Mikael
Top achievements
Rank 1
Iron
answered on 24 Oct 2024, 02:49 PM | edited on 24 Oct 2024, 02:50 PM

Hi Konstantin,

Thank you for your help it works!

I have one more problem with another grid that seems to be the same problem. I have a grid That have a detail which is another grid. Both grid can be edit the same way has my other exemple. I added the react context to both grid and the delete works correctly but when I want to change the editable cell i nedd to click 2 times one that blur and the other to enter. It the same for my add button that is in the toolbar.

My detail grid is a component that I export from another file so it's not inline do I need to change the way I load the detail?

here is my code. First my main grid(i removed a couple of thing because I don't think they are part of the problem)

import DetailGrid from "./components/DetailGrid"
import { CellRender, RowRender, GridContext, RemoveCellRender } from "./components/CustomRender";

function MainGrid() {
const EXPANDED_FIELD = "expanded"
const EDIT_FIELD = "inEdit";

function updateDetail(rowData, dataItem) {
    setstate(dataItem)
}

const customCellRender = (td, props) => (
    <CellRender
        originalProps={props}
        td={td}
        enterEdit={() =>setstate(/*add edit*/)}
        editField={EDIT_FIELD}
    />
);
const customRowRender = (tr, props) => (
    <RowRender
        originalProps={props}
        tr={tr}
        exitEdit={() =>setstate(/*remove edit*/) }
        editField={EDIT_FIELD}
    />
);

return (
             <GridContext.Provider value={{ removeRow: deleteEquipementGroupeQuestionConfirm }}>
                 <Grid
                     data={gridData}
                     total={gridData.length}
                     detail={(props) => (
                         <DetailGrid
                             {...props}
                             onUpdateDetail={updateDetail}
                         />
                     )}
                     expandField={EXPANDED_FIELD}
                     onExpandChange={expandChange}
                     cellRender={customCellRender}
                     rowRender={customRowRender}
                     editField={EDIT_FIELD}
                     onItemChange={gridChange}
                 >
                     <GridToolbar>
                         <Button className="greenIcon" onClick={AddToGrid}><span className="k-icon k-font-icon k-i-plus-outline k-icon-md"></span> Add</Button>
                     </GridToolbar>
                     <Column field="field" title="field" />
                     <Column cells={{
                         data: RemoveCellRender,
                     }} width="100px"></Column>
                 </Grid>
             </GridContext.Provider>
 );
}

 

And here is my detailGrid

import { CellRender, RowRender, GridContext, RemoveCellRender } from "../components/TelerikCustomRender";

function DetailGrid(props) {
 const [detailList, setdetailList] = useState(props.dataItem.detailList || []);
 const [isPending, startTransition] = useTransition();
const EDIT_FIELD = "inEdit";

function AddNewItem() {
    startTransition(() => {
        setdetailList(/*newDetailList*/);
        props.onUpdateDetail(props.dataItem, /*newDetailList*/);
    });
}

function deleteItem() {
    startTransition(() => {
        setdetailList(/*newDetailList*/);
        props.onUpdateDetail(props.dataItem, /*newDetailList*/);
    });
}

const customCellRender = (td, props) => (
    <CellRender
        originalProps={props}
        td={td}
        enterEdit={(dataItem, field) => setdetailList(/*enterEditField*/)}
        editField={EDIT_FIELD}
    />
);
const customRowRender = (tr, props) => (
    <RowRender
        originalProps={props}
        tr={tr}
        exitEdit={onBlurEdit}
        editField={EDIT_FIELD}
    />
);

function itemChange(e) {
    let field = e.field || "";
    e.dataItem[field] = e.value;
    setdetailList(/*newDetailList*/);
}

function onBlurEdit() {
    startTransition(() => {
        setEquipementQuestionList(/*exitEdit*/);
        props.onUpdateDetail(props.dataItem, /*exitEdit*/);
    });
}

return (
        <GridContext.Provider value={{ removeRow: deleteItem}}>
            <Grid
                data={detailList}
                dataItemKey={"detailId"}
                total={detailList.length}
                onItemChange={itemChange}
                cellRender={customCellRender}
                rowRender={customRowRender}
                editField={EDIT_FIELD}
            >
                <GridToolbar>
                    <Button className="greenIcon" onClick={AddNewItem}><span className="k-icon k-font-icon k-i-plus-outline k-icon-md"></span> Add</Button>
                </GridToolbar>
                <Column field="field" title="field" />
                <Column cells={{
                    data: RemoveCellRender,
                }} width="100px"></Column>
            </Grid>
        </GridContext.Provider>
);
}

Konstantin Dikov
Telerik team
commented on 28 Oct 2024, 09:36 AM

Hi Mikael,

You can try setting the detail directly to the custom component:

detail={DetailGrid}

For the additional references you can once again use the React Context.

If the issue persist, please try to isolate the issue in a stackblitz example that we can test and debug locally.

 

Mikael
Top achievements
Rank 1
Iron
commented on 28 Oct 2024, 01:47 PM

Hi Konstantin,

Passing the component directly works with a Context for the update method.

Thank you very much.

Tags
Grid
Asked by
Mikael
Top achievements
Rank 1
Iron
Answers by
Konstantin Dikov
Telerik team
Mikael
Top achievements
Rank 1
Iron
Share this question
or