reactjsdatagriddevexpressdevextremedevextreme-react

Devexpress DataGrid child object editing in React


I have a DataGrid anche inside it I have a child array. It's defined like:

{
id: 2,
attachments: [
{attachmentId: 1001, mandatory: true, name: "ordine"}, 
{attachmentId: 1002, mandatory: true, name: "ordine 2"} ]
..and so on with other fields..
}

Now I want to edit it in mode="form" and I'm not understanding how can I define a editCellComponent to allow the user to add, remove or edit for attachments.

I'm trying to create a Component to manage it with no luck.

interface Props {
    data: any
}

export const AttachmentDataGrid : React.FC<Props> = ({ data } : Props) => {
  const [ attachments, setAttachments ] = useState<any[]>([]);

  useEffect(() => {
    setAttachments(data.value);
  }, []);

  const onValueChanged = (e : any) => {
    data.setValue(e.value);
  }

  const onSelectionChanged = () => {
    data.component.updateDimensions();
  }

  return <>
            <GroupItem caption="Attachment"
              name="phones-container">
                  { attachments.map((att, index) => {
                      return <GroupItem
                      name="attachments"> 
                       <Label text={`Attachments ${index + 1}`} />
                       {att.name}
                                  <SimpleItem dataField={`attachments[${index}].name`} />
                                  <SimpleItem dataField={`attachments[${index}].mandatory`} />
                      </GroupItem>
                    })}
              <SimpleItem itemType="button"
                cssClass="add-attachment-button"
                 onClick={() => { console.log('add-attachment') }}> 
                >
              </SimpleItem>
            </GroupItem>
         
          </> 
  
}

This component show me nothing T_T Did someone achieve something like this and can share code to let me understand how to control it?

I want something like this enter image description here


Solution

  • You can handle a React Component inside the editCellComponent template, where the callback provided by the devexpress component allows you to handle a nested state and to notify the DataGrid that a row state change is requested.

    In case the user will confirm the Form Changes, the changes of the underlying component will be saved, you can check this Sandbox for an example: https://codesandbox.io/s/row-editing-and-editing-events-devextreme-data-grid-forked-my6phn?file=/App.js:2470-3127

    Summarizing, the relevant part is the column:

    <Column
                dataField="Attachments"
                width={250}
                cellRender={(v) => (
                  <span>
                    {v.value
                      ?.map(
                        (i) =>
                          `${i.name} - ${
                            i.mandatory ? "mandatory" : "not mandatory"
                          }`
                      )
                      .join("; ")}
                  </span>
                )}
                editCellComponent={({ data }) => {
                  const { value, setValue } = data;
    
                  return (
                    <AttachmentsGridEditor value={value} setValue={setValue} />
                  );
                }}
              />
    

    And the component itself (please consider these as pure examples, not ready for production usage):

    import { useCallback, useEffect, useState } from "react";
    
    export default function AttachmentsGridEditor({ value, setValue }) {
      const [localState, setLocalState] = useState(value);
    
      const triggerSampleCheckboxChange = useCallback(
        (idx) => {
          const vCopy = [...localState];
          vCopy[idx] = {
            ...vCopy[idx],
            mandatory: !vCopy[idx].mandatory
          };
    
          setLocalState([...vCopy]);
        },
        [localState, setLocalState]
      );
    
      // Because DevExpress does not trigger component re-render, a local state
      // has to be kept to allow sync between the two states.
      useEffect(() => {
        setValue([...localState]);
      }, [localState, setValue]);
    
      // TODO: normalize components, compute a better key
      return (
        <div>
          {localState.map((attachment, attachmentIndex) => (
            <div key={"attachment" + attachmentIndex + Math.random()}>
              <input type="text" readOnly defaultValue={attachment.name} />
              <input
                type="checkbox"
                defaultChecked={attachment.mandatory}
                onChange={() => triggerSampleCheckboxChange(attachmentIndex)}
              />
            </div>
          ))}
        </div>
      );
    }