Search code examples
reactjsdevextreme

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


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} />;
};

Solution

  • 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.