How to add a button inside a cell in the DevExtreme React Grid?

user2952265 picture user2952265 · Jun 13, 2018 · Viewed 7.1k times · Source

I'm using the Def Extreme Grid and I want to have a button in each row, making it possible to change the data of a row. In the example on buttonClick I want to reset the car brand to an empty string. As the custom cell with the button is defined outside the class with the grid I don't know how to acces the state.

Code Pen

const Cell = props => {
  if (props.column.name === "city") {
    return (
      <td>
        <button>Reset car brand</button>
      </td>
    );
  }

  return <Table.Cell {...props} />;
};

Answer

Richie picture Richie · Jun 17, 2018

I'm currently working with react grid from DevExtreme and I've faced a similar problem but followed a different solution. What I did was create a new Plugin in order to add a new column called "ActionsColumn". You can pass both nodes and associated callbacks to the plugin. The minimum code would be something like this (sorry, not tested):

import React from 'react'
import PropTypes from 'prop-types'
import IconButton from '@material-ui/core/IconButton'
import { TABLE_HEADING_TYPE } from '@devexpress/dx-grid-core'
import {Getter, Template, Plugin} from '@devexpress/dx-react-core'
import {
    Table,
} from '@devexpress/dx-react-grid-material-ui'

const pluginDependencies = [
    {name: 'Table'},
];

const ACTIONS_COLUMN_TYPE = 'actionsColumnType';

function tableColumnsWithActions(tableColumns, width) {
    return [...tableColumns, {key: ACTIONS_COLUMN_TYPE, type: ACTIONS_COLUMN_TYPE, width: width}];
}

function isHeadingActionsTableCell(tableRow, tableColumn) {
    return tableRow.type === TABLE_HEADING_TYPE && tableColumn.type === ACTIONS_COLUMN_TYPE;
}

function isActionsTableCell(tableRow, tableColumn) {
    return tableRow.type !== TABLE_HEADING_TYPE && tableColumn.type === ACTIONS_COLUMN_TYPE;
}

export class ActionsColumn extends React.PureComponent {
    render() {
        const {
            actions,
            width,
        } = this.props;
        const tableColumnsComputed = ({tableColumns}) => tableColumnsWithActions(tableColumns, width);

        return (
            <Plugin
                name="ActionsColumn"
                dependencies={pluginDependencies}
            >
                <Getter name="tableColumns" computed={tableColumnsComputed}/>

                <Template
                    name="tableCell"
                    predicate={({tableRow, tableColumn}) =>
                    isHeadingActionsTableCell(tableRow, tableColumn)}
                >
                    <Table.Cell>Actions Column</Table.Cell>
                </Template>
                <Template
                    name="tableCell"
                    predicate={({tableRow, tableColumn}) => isActionsTableCell(tableRow, tableColumn)}
                >
                    {params => (
                        <Table.Cell {...params} row={params.tableRow.row}>
                            {actions.map(action => {
                                const id = params.tableRow.row.id;
                                return (
                                    <IconButton onClick={() => action.action(id)}>
                                        {action.icon}
                                    </IconButton>
                                )

                            })}
                       </Table.Cell>
                    )}
                </Template>
            </Plugin>
        );
    }
}
ActionsColumn.propTypes = {
    actions: PropTypes.arrayOf(PropTypes.PropTypes.shape({
        icon: PropTypes.node,
        action: PropTypes.func.isRequired
    })).isRequired,
    width: PropTypes.number
};
ActionsColumn.defaultProps = {
    width: 240,
};

You simply check whether you are in a header row or a regular table row and you just add a header or the actions you defined respectively.

In order to use this plugin, simply include it in your Grid definition after the Table plugin declaration:

render() {
    const {columns, rows} = this.state;
    const actions = [
        {
            icon: <DeleteIcon/>,
            action: doAlert
        },
        {
            icon: <EditIcon/>,
            action: id => alert('edit id: ' + id)
        }
    ];

    return (
        <Grid rows={rows} columns={columns} getRowId={getRowId}>
            <Table/>
            <TableHeaderRow/>
            <ActionsColumn actions={actions}/>
        </Grid>
    )
}

The way I came up with this solution is quite straightforward:

  1. Read "DevExtreme React Grid - Blog Series" by Oliver Sturm
  2. Read React Core documentation.
  3. Check the existing table-edit-column source code at GitHub: here and here

Hope this helps.