fuat
fuat

Reputation: 1842

How to set row data when clicking button inside the row material-ui DataGrid?

I have a Material-UI DataGrid component that has edit and delete buttons in a row, but I can only do alert or console log,

Why there are buttons in a row because need dialogs when clicking the button, for example when the user clicks the delete button there will be a confirm dialog for delete action, or when click an edit button there will be a form dialog that updates the row,

Need to push the user to edit or delete only one item at the same time. So multiple selection is not allowed.

My question is how to get the row data in the component when clicking the button inside the column definition, in that case, click the Edit or Delete button.

Using "@material-ui/data-grid": "^4.0.0-alpha.26"

import Container from "@material-ui/core/Container";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper";
import DeleteIcon from "@material-ui/icons/Delete";
import EditIcon from "@material-ui/icons/Edit";
import Button from "@material-ui/core/Button";
import AddIcon from "@material-ui/icons/Add";
import { makeStyles } from "@material-ui/core/styles";
import {
  DataGrid,
  GridColDef,
  GridApi,
  GridCellValue
} from "@material-ui/data-grid";

const columns: GridColDef[] = [
  { field: "id", headerName: "ID", width: 70 },
  { field: "name", headerName: "First name", width: 130 },
  { field: "surname", headerName: "Last name", width: 130 },
  {
    field: "edit",
    headerName: "Edit",
    sortable: false,
    width: 130,
    disableClickEventBubbling: true,
    renderCell: (params) => {
      const onClick = () => {
        const api: GridApi = params.api;
        const fields = api
          .getAllColumns()
          .map((c) => c.field)
          .filter((c) => c !== "__check__" && !!c);
        const thisRow: Record<string, GridCellValue> = {};

        fields.forEach((f) => {
          thisRow[f] = params.getValue(f);
        });

        // set to state rather then alert
        return alert(JSON.stringify(thisRow, null, 4));
      };
      return (
        <Button
          variant="contained"
          color="primary"
          startIcon={<EditIcon />}
          onClick={onClick}
        >
          Edit
        </Button>
      );
    }
  },
  {
    field: "delete",
    headerName: "Delete",
    sortable: false,
    width: 130,
    disableClickEventBubbling: true,
    renderCell: (params) => {
      const onClick = () => {
        const api: GridApi = params.api;
        const fields = api
          .getAllColumns()
          .map((c) => c.field)
          .filter((c) => c !== "__check__" && !!c);
        const thisRow: Record<string, GridCellValue> = {};

        fields.forEach((f) => {
          thisRow[f] = params.getValue(f);
        });

        // set to state rather then alert
        return alert(JSON.stringify(thisRow, null, 4));
      };
      return (
        <Button
          variant="contained"
          color="secondary"
          startIcon={<DeleteIcon />}
          onClick={onClick}
        >
          Delete
        </Button>
      );
    }
  }
];

const rows = [
  { id: 1, name: "Example 1", surname: "example" },
  { id: 2, name: "Example 2", surname: "example" }
];

export type User = {
  id: number;
  name: string;
  surname: string;
};

const useStyles = makeStyles((theme) => ({
  content: {
    flexGrow: 1,
    height: "100vh",
    overflow: "auto"
  },
  appBarSpacer: theme.mixins.toolbar,
  container: {
    paddingTop: theme.spacing(4),
    paddingBottom: theme.spacing(4)
  },
  paper: {
    padding: theme.spacing(2),
    display: "flex",
    overflow: "auto",
    flexDirection: "column",
    elevation: 3
  }
}));

const App = () => {
  const classes = useStyles();

  return (
    <main className={classes.content}>
      <div className={classes.appBarSpacer} />
      <Container maxWidth="lg" className={classes.container}>
        <Grid item xs={12}>
          <Paper className={classes.paper}>
            <DataGrid
              rows={rows}
              columns={columns}
              pageSize={10}
              columnBuffer={8}
              autoHeight
            />
          </Paper>
        </Grid>
        <Grid item xs={3} style={{ padding: 20 }}>
          <Button
            variant="contained"
            color="primary"
            startIcon={<AddIcon />}
            onClick={() => {
              console.log("new");
            }}
          >
            New
          </Button>
        </Grid>
      </Container>
    </main>
  );
};

export default App;

Upvotes: 0

Views: 14254

Answers (2)

fuat
fuat

Reputation: 1842

DataGrid component has a prop onCellClick to handle event when user clicks any cell in the data grid. This prop use GridCellParams type, just need to filter columns base on GridCellParams.colDef.field attribute.

Code changed as below;

Edit Styled Component

EDIT: According to the Material-UI documentation, GridCellParams property getValue just changed. Now it takes two-parameter; getValue(id: GridRowId, field: string)

import Container from "@material-ui/core/Container";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper";
import DeleteIcon from "@material-ui/icons/Delete";
import EditIcon from "@material-ui/icons/Edit";
import Button from "@material-ui/core/Button";
import { makeStyles } from "@material-ui/core/styles";
import {
  DataGrid,
  GridColDef,
  GridApi,
  GridCellValue,
  GridCellParams
} from "@material-ui/data-grid";
import React, { useState } from "react";
import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogContentText,
  DialogActions
} from "@material-ui/core";

const columns: GridColDef[] = [
  { field: "id", headerName: "ID", width: 70 },
  { field: "name", headerName: "First name", width: 130 },
  { field: "surname", headerName: "Last name", width: 130 },
  {
    field: "edit",
    headerName: "Edit",
    sortable: false,
    width: 130,
    disableClickEventBubbling: true,
    renderCell: () => {
      return (
        <Button variant="contained" color="primary" startIcon={<EditIcon />}>
          Edit
        </Button>
      );
    }
  },
  {
    field: "delete",
    headerName: "Delete",
    sortable: false,
    width: 130,
    disableClickEventBubbling: true,
    renderCell: () => {
      return (
        <Button
          variant="contained"
          color="secondary"
          startIcon={<DeleteIcon />}
        >
          Delete
        </Button>
      );
    }
  }
];

const rows = [
  { id: 1, name: "Example 1", surname: "example" },
  { id: 2, name: "Example 2", surname: "example" }
];

export type User = {
  id: number;
  name: string;
  surname: string;
};

const useStyles = makeStyles((theme) => ({
  content: {
    flexGrow: 1,
    height: "100vh",
    overflow: "auto"
  },
  appBarSpacer: theme.mixins.toolbar,
  container: {
    paddingTop: theme.spacing(4),
    paddingBottom: theme.spacing(4)
  },
  paper: {
    padding: theme.spacing(2),
    display: "flex",
    overflow: "auto",
    flexDirection: "column",
    elevation: 3
  }
}));

const App = () => {
  const classes = useStyles();
  const [selectedUser, setSelectedUser] = useState({} as User);
  const [openDialog, setOpenDialog] = useState(false);

  function currentlySelected(params: GridCellParams) {
    const api: GridApi = params.api;
    const value = params.colDef.field;

    if (!(value === "edit" || value === "delete")) {
      return;
    }

    const fields = api
      .getAllColumns()
      .map((c) => c.field)
      .filter((c) => c !== "__check__" && !!c);
    const thisRow: Record<string, GridCellValue> = {};

    fields.forEach((f) => {
      thisRow[f] = params.getValue(params.id, f);
    });

    const user = {} as User;
    user["id"] = Number(thisRow["id"]);
    user["name"] = thisRow["name"]!.toString();
    user["surname"] = thisRow["surname"]!.toString();

    setSelectedUser(user);
    setOpenDialog(true);
  }

  const handleClose = () => {
    setOpenDialog(false);
  };

  return (
    <main className={classes.content}>
      <div className={classes.appBarSpacer} />
      <Container maxWidth="lg" className={classes.container}>
        <Grid item xs={12}>
          <Paper className={classes.paper}>
            <DataGrid
              rows={rows}
              columns={columns}
              pageSize={10}
              columnBuffer={8}
              autoHeight
              onCellClick={currentlySelected}
            />
          </Paper>
        </Grid>
      </Container>
      <Dialog
        open={openDialog}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        <DialogTitle id="form-dialog-title"> User </DialogTitle>
        <DialogContent>
          <DialogContentText id="alert-dialog-description">
            {JSON.stringify(selectedUser)}
          </DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Cancel
          </Button>
          <Button onClick={handleClose} color="primary">
            Confirm
          </Button>
        </DialogActions>
      </Dialog>
    </main>
  );
};

export default App;

Upvotes: 2

Mohamed Jakkariya
Mohamed Jakkariya

Reputation: 1627

Update - 2021

06/08/2021 - Latest answer

The following ones work for me as expected.

const columns: GridColDef[] = [
  { field: 'id', headerName: 'ID', width: 100, hide: true },
  {
    field: 'action',
    width: 130,
    sortable: false,

    renderCell: (params) => {
      const onClickDelete = async () => {
        return alert(JSON.stringify(params.row, null, 4));
      };
      const onClickEdit = async () => {};

      return (
        <>
          <IconButton color="secondary" onClick={onClickDelete}>
            <DeleteIcon />
          </IconButton>
          <IconButton color="secondary" onClick={onClickEdit}>
            <EditIcon />
          </IconButton>
        </>
      );
    },
  },

Upvotes: 0

Related Questions