Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[data grid] Different field in view and edit mode cell #13103

Closed
mickbigblue opened this issue May 13, 2024 · 9 comments
Closed

[data grid] Different field in view and edit mode cell #13103

mickbigblue opened this issue May 13, 2024 · 9 comments
Labels
component: data grid This is the name of the generic UI component, not the React module! customization: extend Logic customizability feature: Editing Related to the data grid Editing feature status: waiting for author Issue with insufficient information support: commercial Support request from paid users support: premium standard Support request from a Premium standard plan user. https://mui.com/legal/technical-support-sla/

Comments

@mickbigblue
Copy link

mickbigblue commented May 13, 2024

The problem in depth

Background

Our application receives data objects from the backend containing various "checkpoint" fields that are aggregated and displayed in a single column, but these fields are not represented as individual columns. For example, each object includes:

{
  "planCP": {
    "id": 123,
    "name": "myPlanCPname"
  },
  "dueCP": "myDueCPname",
  "vPlanCP": "myvPlanCPname"
}

Current Implementation

In the UI, we have an "allCPs" column that displays these checkpoint names concatenated as follows:

allCPs
-------------------
P: myPlanCPname
D: myDueCPname
vP: myvPlanCPname

When entering edit mode (via double click or an edit action button), the goal is for the user to modify the planCP.name field. However, the edit impacts the entire allCPs field because that's how the column is defined.

I now want to be able to let the user double click (or click the edit action button) and edit the cell in the allCPs column but the change should apply to the planCP.name (in this example "myPlanCPname" field in the object used for that row.

This is my current implementation for the custom cell edit renderer but my change is obviously not used to update planCP.name but the whole allCPs (because that's the field in the column definition).

import { TextField } from '@mui/material';
import {
  GridRenderEditCellParams,
  useGridApiContext,
} from '@mui/x-data-grid-premium';
import React, { useRef, useLayoutEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';

const EditPlanCPCell = (props: GridRenderEditCellParams) => {
  const { id, field, hasFocus, row, label } = props;
  const apiRef = useGridApiContext();
  const inputRef = useRef<HTMLInputElement>(null);
  const { t } = useTranslation();
  const [value, setValue] = useState(row.planCP.name);

  useLayoutEffect(() => {
    if (hasFocus) {
      inputRef.current?.focus();
    }
  }, [hasFocus]);

  const handleValueChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const newValue = event.target.value;
    console.log('Changing value to: ', newValue);
    setValue(newValue);
  };

  return (
    <TextField
      ref={inputRef}
      label={t(label)}
      value={value || ''}
      onChange={handleValueChange}
      onBlur={() => apiRef.current.setEditCellValue({ id, field, value })}
    />
  );
};

export default EditPlanCPCell;

Question

What is the best practice for configuring a DataGrid column to display aggregated data in view mode but allow editing of a specific, related field within the data object in edit mode? Specifically, we need the edit actions within the "allCPs" column to update the planCP.name field instead of the aggregated allCPs field.

Any guidance or recommendations on how to achieve this behavior would be greatly appreciated.
Thanks in advance!
Mick

Your environment

`npx @mui/envinfo`
I am using Firefox.
  System:
    OS: Linux 5.15 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
  Binaries:
    Node: 18.17.1 - /usr/bin/node
    npm: 10.5.2 - /usr/bin/npm
    pnpm: Not Found
  Browsers:
    Chrome: Not Found
  npmPackages:
    @emotion/react: ^11.11.4 => 11.11.4 
    @emotion/styled: ^11.11.5 => 11.11.5 
    @mui/base:  5.0.0-beta.40 
    @mui/core-downloads-tracker:  5.15.17 
    @mui/icons-material: ^5.15.15 => 5.15.17 
    @mui/material: ^5.15.11 => 5.15.17 
    @mui/private-theming:  5.15.14 
    @mui/styled-engine:  5.15.14 
    @mui/system:  5.15.15 
    @mui/types:  7.2.14 
    @mui/utils:  5.15.14 
    @mui/x-data-grid:  7.4.0 
    @mui/x-data-grid-premium: ^7.2.0 => 7.4.0 
    @mui/x-data-grid-pro:  7.4.0 
    @mui/x-date-pickers: ^7.2.0 => 7.4.0 
    @mui/x-license:  7.2.0 
    @types/react: ^18.2.79 => 18.3.2 
    react: ^18.2.0 => 18.3.1 
    react-dom: ^18.2.0 => 18.3.1 
    typescript: ^4.9.5 => 4.9.5 

Search keywords: editing non column values
Order ID: 80654

@mickbigblue mickbigblue added status: waiting for maintainer These issues haven't been looked at yet by a maintainer support: commercial Support request from paid users labels May 13, 2024
@zannager zannager added component: data grid This is the name of the generic UI component, not the React module! support: premium standard Support request from a Premium standard plan user. https://mui.com/legal/technical-support-sla/ labels May 13, 2024
@michelengelen michelengelen changed the title Different field in view and edit mode cell [data grid] Different field in view and edit mode cell May 14, 2024
@michelengelen michelengelen added feature: Editing Related to the data grid Editing feature customization: extend Logic customizability labels May 14, 2024
@michelengelen
Copy link
Member

Hey @mickbigblue ... this should be possible with using a combination of valueSetter, valueGetter and valueParser (from this section of the docs).

Let me give this a shot and I'll circle back here.

@michelengelen
Copy link
Member

OK, I found a solution that might be a good fit for you case: Example

I did achieve this with a custom cell renderer, custom edit cell renderer and valueFormatter to display the data.

Let me know what you think!

@michelengelen michelengelen added status: waiting for author Issue with insufficient information and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels May 14, 2024
@mickbigblue
Copy link
Author

mickbigblue commented May 15, 2024

Hi @michelengelen.

Thanks for your help!
This looks like a good approach to me,but there are still some issues I'm facing.

I get the Backend-Data in a specific Format like:

interface BackendSection {
  id: number;
  name: string;
  guiDisplayNumber: number;
  planCheckpoint: {
        id: number;
        name: string;
  } | null;
  dueCheckpoint?: string;
  virtualPlanCheckpoint?: string;
  virtualDueCheckpoint?: string;
}

My column definition for the grid is this:

    columns: [
      {
        field: 'id',
        headerName: 'ID',
      },
      {
        field: 'guiDisplayNumber',
        headerName: 'No.',
        editable: true,
      },
      {
        field: 'name',
        headerName: 'Name',
        editable: true,
      },
      {
        field: 'allCPs',
        headerName: 'AllCPs',
        editable: true,
      },
    ],

So in the grid we have these columns where allCPs is a combination of the checkpoints. However the only editable one of them all should be the object planCheckpoint.

If I implement your solution according to the demo, the allCPs array allCPs: Array(4) [ "P: 0101", "D: 5741", "vP: 0718" ] is transformed into an object and the planCP is added to it like so:

{
  "0": "P: 0101",
  "1": "D: 5741",
  "2": "vP: 0718",
  "planCP": {
    "name": "1234"
  }
}

In my current implementation I have a transformator component that takes in the backend data and transforms the separate checkpoint fields into the allCPs array before passing it to the datagrid to render it.

Maybe this is my mistake. Maybe I should use valueGetters and valueFormatters for the formatting and presentation.

Also I could use an invisible column for the planCP object and pass that as the field variable in my renderEditCell method for allCPs.

What do you think?

@github-actions github-actions bot added status: waiting for maintainer These issues haven't been looked at yet by a maintainer and removed status: waiting for author Issue with insufficient information labels May 15, 2024
@michelengelen
Copy link
Member

Well, you basically only have to adjust the example a bit.
This works just as well: DEMO

Right now there is still the Popper in place. This can be replaced with a simple textfield when only one value should be adjusted.
You could even go in and render the complete list of items, but only replace the planCheckpoint name with a textfield to not lose the other information when editing.

Let me know if this works for you.

@michelengelen michelengelen added status: waiting for author Issue with insufficient information and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels May 15, 2024
@mickbigblue
Copy link
Author

This looks quite good to me. Thanks!

I'll need to experiment a little bit with it and will get back to you.

@github-actions github-actions bot added status: waiting for maintainer These issues haven't been looked at yet by a maintainer and removed status: waiting for author Issue with insufficient information labels May 16, 2024
@michelengelen
Copy link
Member

OK, Thanks for letting us know! 👍🏼

@michelengelen michelengelen added status: waiting for author Issue with insufficient information and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels May 16, 2024
@mickbigblue
Copy link
Author

So I've tried out some stuff but I have some more conceptiual questions. I'll try to be as thorough as possible.

As I said, we get the data from the backend in a certain format. Here's a more complete example:

interface BackendSection {
  id: number;
  name: string;
  guiDisplayNumber: number;
  lineId: number;
  densityId: number;
  planCheckpoint: {
        id: number;
        name: string;
  } | null;
  dueCheckpoint?: string;
  virtualPlanCheckpoint?: string;
  virtualDueCheckpoint?: string;
}

The REST call gives back the (main) sections array and some auxiliary arrays. These auxiliary arrays are for the presentation layer. If you look at the updated BackendSection interface you will see stuff like lineId and densityId. As we don't want to show the user IDs in the datagrid we have these auxiliary arrays that map IDs to names.

So a response for sections looks like this:

sections: [
 { id: 1, name: "Section 1", guiDisplaynumber: "1", lineId: "1", planCP: { id: 5, name: "abcd" }, vPlanCP: "1234", ... }
 ...
],
lines: [
 { id: 1, name: "Line 1" },
 { id: 2, name: "Line 2" }.
],
densities: [
  { id: 1, name: "Density 1" },
  { id: 2, name: "Density 2" },

Now, my question is, where do I transform this data into Rows? I need to get the visible names from the auxiliary arrays and present them in the datagrid. But when the user changes the line, I need to send back the changed ID not the name.

So to sum it up:
I'm not sure how the transformation from backend to frontend data (and back) works. Where do I transform the data? Do I transform the backend data to rows with the names from the auxiliary arrays and pass that to the grid, or do I pass the IDs and use some form of valueGetter, valueSetter, valueFormatter, valueParser, renderCell (the exact usage of these still confuses me)? Or do I pass both as rows?

In which format do I supply the data to the datagrid best? And how do I get the data back in a format such that the backend accepts it?

I hope this is not too confusing.

@github-actions github-actions bot added status: waiting for maintainer These issues haven't been looked at yet by a maintainer and removed status: waiting for author Issue with insufficient information labels May 17, 2024
@michelengelen
Copy link
Member

Hey @mickbigblue this is not confusing at all, but slightly out of range for the support, but well ... :P

So for the transformation you want to do that before it gets passed to the datagrid. This has to main benefits:

  1. you are more flexible in transforming the way you need it
  2. the datagrid is more performant when data is pure

Here is a updated demo

Here you can see how that transformation could look like.
It really depends on the structure and what you want to do with it.

You could also provide all lines/densities to a singleselect column where you can then select one. But without knowing what you are trying to achieve this is hard to tell. (and, as mentioned already, a bit outside of the support range :P)

@michelengelen michelengelen added status: waiting for author Issue with insufficient information and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels May 17, 2024
Copy link

The issue has been inactive for 7 days and has been automatically closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: data grid This is the name of the generic UI component, not the React module! customization: extend Logic customizability feature: Editing Related to the data grid Editing feature status: waiting for author Issue with insufficient information support: commercial Support request from paid users support: premium standard Support request from a Premium standard plan user. https://mui.com/legal/technical-support-sla/
Projects
None yet
Development

No branches or pull requests

3 participants