DevExpress / devextreme-reactive

Business React components for Bootstrap and Material-UI
https://devexpress.github.io/devextreme-reactive/
Other
2.07k stars 381 forks source link

Populate Select Component on Editing using data fetched via AJAX? #678

Closed glenpadua closed 6 years ago

glenpadua commented 6 years ago

Current Behaviour

Based on the example here, I am trying use other form fields as editable fields except that I am using Redux and I am trying to populate the dropdown options with values fetched via AJAX.

I am doing this by fetching the values with an action creator inside componentDidMount() and mapping the state to props on the parent Component containing the Grid. How would I make these props available to the LookUpEditCell Component? Or is there a better way to be doing this?

I thought passing it as props to the TableEditRow Component would work, like this.

<TableEditRow
    cellComponent={EditCell}
    moreProps={dropDownValues}
/>

I also tried mapping the state to the props of the LookupEditCell as well like this.

export const LookupEditCell = connect(mapStateToProps, actions)(
  withStyles(styles)(LookupEditCellBase)
);

Expected Behaviour

On editing a row, one of the columns should use a Select Component populated by values fetched using AJAX.

Steps to Reproduce (for Bugs)

  1. Use the same example as found on - https://github.com/DevExpress/devextreme-reactive/blob/v1.0.0-beta.3/packages/dx-react-demos/src/material-ui/featured-controlled-mode/demo.jsx
  2. Use an array of data as props inside the parent component containing the Grid.
  3. Use this data to populate a dropdown field when a row is being edited.

Your Environment

devextreme-reactive: ^1.0.0-beta.3 react: 16.2.0 browser: Chrome 63.0.3239.132 bootstrap: none react-bootstrap: none material-ui: 1.0.0-beta.27

Thanks for the great product! Any help will be much appreciated.

SergeyAlexeev commented 6 years ago

Hi,

As far as I understand your requirements, you can use the following approach:

const LookupEditCellBase = ({
  value, onValueChange, classes, customDropDownItems,
}) => (
  <TableCell
    className={classes.lookupEditCell}
  >
    <Select
      // ...
    >
      {customDropDownItems.map(item => (
        <MenuItem key={item} value={item}>{item}</MenuItem>
      ))}
    </Select>
  </TableCell>
);

const EditCell = (props) => {
  const { customDropDownItems, ...cellProps } = props;
  const availableColumnValues = availableValues[props.column.name];
  if (availableColumnValues) {
    return (
      <LookupEditCell
        {...cellProps}
        customDropDownItems={customDropDownItems}
      />
    );
  }
  return <TableEditRow.Cell {...cellProps} />;
};

class DemoBase extends React.PureComponent {
  constructor(props) {
    //...
    this.customDropDownItems = ['a', 'b', 'c'];
  }
  render() {
    //...
    return (
      <Paper>
        <Grid
          // ...
        >
        {/* ... */}
        <TableEditRow
          cellComponent={cellProps => (
            <EditCell
              {...cellProps}
              customDropDownItems={this.customDropDownItems}
            />
          )}
        />
        </Grid>
      </Paper>
    );
  }
}

Here I add customDropDownItems to EditCell's properties. Then, I use them as selectbox items.

glenpadua commented 6 years ago

That worked perfect thanks! But I am facing another issue now. When I edit a row, the current value in the dropdown menu disappears. When I click on the dropdown to reveal the other items, I can see them but when I try selecting picking an item, my app crashes and the console has this message - Uncaught Error: Material-UI: thevalueproperty is required when using theSelectcomponent with 'native=false'.

So my understanding is that the value for the Select component is not being set both initially during edit and also when it's onChange event executes.

My {rows} look like this

[
    {
      "email": "test@domain.tld", 
      "id": "5a5c26f4c54d2d0366fa801b", 
      "name": "Test", 
      "token": "User-sdf84r89fwer", 
      "user_type": {
        "id": "5a5c25dbc54d2d0366fa801a", 
        "name": "Admin", 
        "permissions": {
          "lists": {
            "create": false, 
            "delete": false, 
            "read": false, 
            "update": false
          }, 
          "phases": {
            "create": false, 
            "delete": false, 
            "read": false, 
            "update": false
          }
        }, 
        "token": "UserType-k32k4kl9sdmfmk"
      }
    }
]

And my {columns} like this

[
  { name: 'name', title: 'Name' },
  { name: 'email', title: 'Email ID' },
  {
    name: 'user_type',
    title: 'User Type',
    getCellValue: row => (row.user_type ? row.user_type.name : undefined)
  },
]

These render properly the first time. The user_type column is what I'm trying to populate with dropdown values. The user_types array looks like this,

[
        {
            "id": "5a5f6aadc54d2d269c0700ce",
            "name": "Admin",
            "permissions": {
                "lists": {
                    "create": true,
                    "delete": true,
                    "read": true,
                    "update": true
                },
                "phases": {
                    "create": true,
                    "delete": true,
                    "read": true,
                    "update": true
                }
            },
            "token": "UserType-k32k4kl9sdmfmk"
        },
        {
            "id": "5a5f6c44c54d2d269c0700cf",
            "name": "Moderator",
            "permissions": {
                "lists": {
                    "create": true,
                    "delete": true,
                    "read": true,
                    "update": true
                },
                "phases": {
                    "create": false,
                    "delete": false,
                    "read": false,
                    "update": false
                }
            },
            "token": "UserType-k32k4kl9sdmfmk"
        },
        {
            "id": "5a5f6cbbc54d2d269c0700d1",
            "name": "Guest",
            "permissions": {
                "lists": {
                    "create": false,
                    "delete": false,
                    "read": true,
                    "update": false
                },
                "phases": {
                    "create": false,
                    "delete": false,
                    "read": false,
                    "update": false
                }
            },
            "token": "UserType-k32k4kl9sdmfmk"
        }
    ]

And finally based on the example and with your help I pass the user_types as props to LookUpEditCellBase and use it like this,

const LookupEditCellBase = ({
  userTypes, value, onValueChange, classes,
}) => (
  <TableCell
    className={classes.lookupEditCell}
  >
    <Select
      value={value}
      onChange={event => onValueChange(event.target.value)}
      input={
        <Input
          classes={{ root: classes.inputRoot }}
        />
      }
    >
      {userTypes.map(item => (
        <MenuItem key={item.id} value={item.id}>{item.name}</MenuItem>
      ))}
    </Select>
  </TableCell>
);

Any idea what might be causing the Select field to not be able to read the value on edit and onChange? Every other piece is pretty much the same as the example along with your help in passing the props down to LookUpEditCellBase. Appreciate the help so far!

Incase this isn't the appropriate place, I've created a Stackoverflow thread as well.

SergeyAlexeev commented 6 years ago

It looks like you didn't specify the createRowChange function for the user_type column:

<EditingState
  // ...
  columnExtensions: [
    { 
      columnName: 'user_type',
      createRowChange: (row, value) => {
         return ({ user_type: { name: value } })
       },
    }
  ]
/>

I've created a sample as well.

glenpadua commented 6 years ago

Ah I missed this part from the docs. Thanks a lot! One last thing. How would I pass another value for the dropdown list that is differs from what is being displayed to the user?

For example I would like to have the user_types id as the value for the <MenuItem> but still display the name to the user.

const LookupEditCellBase = ({
  userTypes, value, onValueChange, classes,
}) => (
  <TableCell
    className={classes.lookupEditCell}
  >
    <Select
      value={value}
      onChange={event => onValueChange(event.target.value)}
      input={
        <Input
          classes={{ root: classes.inputRoot }}
        />
      }
    >
      {userTypes.map(item => (
        <MenuItem key={item.id} value={item.id}>{item.name}</MenuItem>
      ))}
    </Select>
  </TableCell>
);
SergeyAlexeev commented 6 years ago

To implement such a functionality, change the createRowChange and getCellValue functions as follows:  

getCellValue: row => (row.user_type ? row.user_type.id : undefined),

createRowChange: (row, value) => {
  return ({ user_type: { id: value } })
},

  Now, you can use user_type.id as a cell value. To use user_type.name as a cell display value you can use the cellComponent property of the Table plugin as described here.   Please, refer to the updated sample.   Moreover, it looks like I found a small issue in your code. The rows and user_types arrays contain similar 'Admin' items, but their ids are different. I suppose they should be equal.

SergeyAlexeev commented 6 years ago

Also, this article would be helpful in your case. It describes how to use the DataTypeProvider plugin to customize editors and format cells' content.

glenpadua commented 6 years ago

Thanks this looks great! So would using the DataTypeProvider plugin be the preferred approach to do this? Because I seem to have run into another blocker with the above method while adding a new row.

And about the ids. Ones the id of the user and the other is the id of the user_type which could be one of three. Admin happens to be the name of the user as well as the name of the user_type :)

SergeyAlexeev commented 6 years ago

Yes, the DataTypeProvider approach can be more convenient in your case.

glenpadua commented 6 years ago

The DataTypeProvider method worked like a charm. Thanks a lot for your quick responses and help 👍

KaiWynn commented 6 years ago

Can you help me with this issue? I was able to pass the custom dropdown list to (EditCell), but my entire row re-render and I lost the cell focus on every key stroke. Try to change the Sale Amount in this example: https://codesandbox.io/s/zl455vp42p

lock[bot] commented 5 years ago

This thread has been automatically locked since it is closed and there has not been any recent activity. Please open a new issue for related bugs or feature requests.