A project I am involved with has a react user grid component. The grid is populated by requesting data from the server. When I click on a button in a grid row, I need to get the value of the "_id" field of this particular row.
I only managed to get the _id of all rows, but I only need the value of the row where the button was clicked. In addition, the button click event occurs immediately on page load, not just on click.
const columns = [
{
field: '_id', headerName: 'id', type: 'number', flex: 0.9,
},
{
field: 'userName', headerName: 'Username', flex: 0.7,
},
{
field: 'email', headerName: 'email', flex: 0.7,
},
{
field: 'fullName', headerName: 'Full name', flex: 0.7,
},
{
field: 'status', headerName: 'Status', flex: 0.7,
},
{
field: 'actions',
type: 'actions',
headerName: 'Actions',
flex: 0.2,
getActions: (params) => [
<IconButton onClick={console.log(params.row._id)}>
<EditIcon />
</IconButton>,
],
},
];
function generateRows(users) {
return users.map((user) => (({
_id, userName, email, fullName, status,
}) => ({
_id, userName, email, fullName, status,
}))(user));
}
export default function UserControlTable() {
const [data, setData] = useState({
users: [],
});
useEffect(() => {
const fetchUsers = async () => {
const users = await axios.get(process.env.REACT_APP_API_URL + USER_LIST);
setData({ users: generateRows(users.data)});
};
fetchUsers();
}, []);
return (
<Container>
<DataGrid
getRowId={(row) => row._id}
rows={data.users}
columns={columns}
checkboxSelection
column
/>
</Container>
);
}
Like my comment above try:
onClick={() => console.log(params.row._id)}
instead of
onClick={console.log(params.row._id)}
You have to return an arrow function inside the onClick event handler.
Related
What I need: I'm using MUI DataGrid > 5. I need that the data that is filled in a cell change automatically the state (onChange). At this moment, I have an useEffect that is listening to these changes to “enable/disable” the “Save” and “Cancel” buttons.
What I tried: Using onCellEditCommit property, it saves the cell content only when I click outside the DataGrid component (or when I press tab or enter). I would like to save the content on cell change.
...
import {
DataGrid,
GridColDef,
GridPreProcessEditCellProps,
GridRowId,
} from "#mui/x-data-grid";
...
const [devices, setDevices] = useState<Device[]>(formValues.devices);
const columns: GridColDef[] = [
{
field: "id",
headerName: "ID",
flex: 10,
},
{
field: "uniqueIdentification",
headerName: "Unique ID",
editable: true,
flex: 30,
},
{
field: "description",
headerName: "Description",
editable: true,
flex: 50,
},
{
field: "isActive",
headerName: "Active",
type: "boolean",
editable: true,
flex: 10,
},
{
field: "actions",
type: "actions",
headerName: "Actions",
width: 100,
cellClassName: "actions",
renderCell: (params) => (
<Box id={`${params.id}`} component="div">
<IconButton
title="Remove"
onClick={() => deleteRow(params.id)}
size="small"
>
<DeleteIcon />
</IconButton>
</Box>
),
},
];
function saveDeviceCell(params) {
const oldDevices = [...devices];
const rowDeviceIndex = oldDevices.findIndex((dev) => dev.id === params.id);
oldDevices[rowDeviceIndex] = {
...oldDevices[rowDeviceIndex],
[params.field]: params.value,
};
setDevices(oldDevices);
}
...
return (
<DataGrid
rows={devices}
columns={columns}
hideFooterPagination
hideFooter
disableSelectionOnClick
autoHeight
onCellEditCommit={saveDeviceCell}
editMode="cell"
/>
...
);
I have multiple DataGrid tables in my project, but I cannot figure out what is wrong with this one.
I have created a codesandbox example of my problem. If anyone could help I would appreciate it very much.
It is probably a dumb mistake
codesandbox example
You have declared a field with name license 2 times.
Changing to e.g.
{
field: "licence",
headerName: "Licence start",
flex: 1,
valueGetter: (params) =>
`${moment(params.row.licence.startsAt).format("DD.MM.YYYY") || ""}`
},
{
field: "licence2",
headerName: "Licence ends at",
flex: 1,
valueGetter: (params) =>
`${moment(params.row.licence.endsAt).format("DD.MM.YYYY") || ""}`
},
will solve the problem
You can use the params.id to look for that particular element in the valueGetter function and then return it. I've answered a similar question here.
const columns = React.useMemo(
() => [
..., // all the other elements
// No changes needed here since this is the first occurrence of 'details'
{
field: 'details',
headerName: 'Ready By',
type: 'datetime',
valueGetter: ({ value }) => value.ready_by && new Date(value.ready_by),
width: 250,
},
// Here we're basically searching for the item of interest since we do get `id` as a param arg.
{
field: 'details2',
headerName: 'Name',
valueGetter: ({ id }) => {
const item = data.find(item => item.id === id);
return item.name;
},
width: 250,
},
],
[data]
)
I am resolved with this solution:
{ field: 'id', headerName: 'No', flex: 0.3, minWidth: 50 },
{
field: 'displayName',
headerName: 'Name',
flex: 1.2,
minWidth: 160,
renderCell: ({ id }) => {
const item = users.find(item => item.id === id);
return (
<>
<Avatar alt="avatar" src={item.avatarUrl} />
<span className="list-user-name" onClick={handleShowDetailUser}>
{item.displayName}
</span>
</>
);
},
},
so the error is because you’re having those bothe fileds at the time (same name),
{
field: "licence",
headerName: "Licence start",
flex: 1,
valueGetter: (params) =>
`${moment(params.row.licence.startsAt).format("DD.MM.YYYY") || ""}`
},
{
field: "licence",
headerName: "Licence ends at",
flex: 1,
valueGetter: (params) =>
`${moment(params.row.licence.endsAt).format("DD.MM.YYYY") || ""}`
},
removing one of them will solve your issue , you need to make sure how to access the date in other way
I am trying to implement crud functionalities in Ant design table. I observed that the delete and edit functionalities only works on the instance when I perform the operations on initial render, but after reloading the component, the table returns back to its initial state and the operations don't affect the database in any way.
I see this error on my console
Type '{ title: string; dataIndex: string; key: string; align: string; editable: boolean;
render?: undefined; }' is not assignable to type 'ColumnType<any>'.
Types of property 'align' are incompatible.
Type 'string' is not assignable to type 'AlignType'.
These are the codes below, I hope to break them down in code blocks so they can be understandable
imports
import React, { useState, useEffect } from 'react';
import { Table, Popconfirm, Button, Space, Input, Form } from 'antd';
import { isEmpty } from 'lodash';
api
const apiUrl = 'api/terminals';
useState and useEffect codes
const [gridData, setGridData] = useState([]);
const [loader, setLoader] = useState(false);
const [editingKey, setEditingKey] = useState('');
const [editRow, setEditRow] = useState(false);
const [form] = Form.useForm();
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
setLoader(true);
const response = await axios.get(apiUrl);
setGridData(response.data);
setLoader(false);
};
modifiedData codes
const modifiedData = gridData.map(({ ...item }) => ({
...item,
key: item.id,
}));
const save = async key => {
try {
const row = await form.validateFields();
const newData = [...modifiedData];
const index = newData.findIndex(item => key === item.key);
if (index > -1) {
const item = newData[index];
newData.splice(index, 1, { ...item, ...row });
setGridData(newData);
setEditingKey('');
}
} catch (error) {
console.warn('Error', error);
}
};
edit and cancel function code block
const edit = record => {
form.setFieldsValue({
name: '',
stock: '',
stockLevelDate: '',
tankThreatLevel: '',
tankThreatLevelColor: '',
tankTopPosition: '',
tankTopPositionColor: '',
lowPumpable: '',
lowPumpableColor: '',
tankCapacity: '',
...record,
});
setEditingKey(record.key);
};
const cancel = () => {
setEditingKey('');
};
editableCell function block
const EditableCell = ({ editing, dataIndex, title, record, children, ...restProps }) =>
{
const input = <Input />;
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
style={{ margin: 0 }}
rules={[
{
required: true,
message: `Please input ${title}`,
},
]}
>
{input}
</Form.Item>
) : (
children
)}
</td>
);
};
editing code block
const isEditing = record => {
return record.key === editingKey;
};
columns block
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
},
{
title: 'Name' as string,
dataIndex: 'name',
key: 'name',
align: 'center',
editable: true,
},
{
title: 'Stock',
dataIndex: 'stock',
key: 'stock',
align: 'center',
editable: true,
},
{
title: 'Stock Level Date',
dataIndex: 'stockLevelDate',
key: 'stockLevelDate',
align: 'center',
editable: true,
},
{
title: 'Tank Threat Level',
dataIndex: 'tankThreatLevel',
key: 'tankThreatLevel',
align: 'center',
editable: true,
},
{
title: 'Tank Threat Level Color',
dataIndex: 'tankThreatLevelColor',
key: 'tankThreatLevelColor',
align: 'center',
editable: true,
},
{
title: 'Tank Top Position',
dataIndex: 'tankTopPosition',
key: 'tankTopPosition',
align: 'center',
editable: true,
},
{
title: 'Tank Top Position Color',
dataIndex: 'tankTopPositionColor',
key: 'tankTopPositionColor',
align: 'center',
editable: true,
},
{
title: 'Low Pumpable',
dataIndex: 'lowPumpable',
key: 'lowPumpable',
align: 'center',
editable: true,
},
{
title: 'Low Pumpable Color',
dataIndex: 'lowPumpableColor',
key: 'lowPumpableColor',
align: 'center',
editable: true,
},
{
title: 'Tank Capacity',
dataIndex: 'tankCapacity',
key: 'tankCapacity',
align: 'center',
editable: true,
},
{
title: 'Actions',
dataIndex: 'actions',
key: 'actions',
align: 'center',
render: (_, record) => {
const editable = isEditing(record);
return modifiedData.length >= 1 ? (
<Space>
<Popconfirm title="Sure to delete?" onConfirm={() => handleDelete(record)}>
<Button type="primary" disabled={editable} danger>
Delete
</Button>
</Popconfirm>
{editable ? (
<span>
<Space size="middle">
<Button onClick={e => save(record.key)} type="primary" style={{ marginRight: 8
}}>
{' '}
Save
</Button>
<Popconfirm title="Sure to cancel?" onConfirm={cancel}>
<Button>Cancel</Button>
</Popconfirm>
</Space>
</span>
) : (
<Button onClick={() => edit(record)} type="primary">
Edit
</Button>
)}
</Space>
) : null;
},
},
];
mergedCoulumns block
const mergedColumns = columns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
handleDelete code block
const handleDelete = value => {
const dataSource = [...modifiedData];
const filteredData = dataSource.filter(item => item.id !== value.id);
setGridData(filteredData);
};
return jsx
<>
<div>
<h2>Terminals</h2>
<Link to={`${match.url}/new`} className="btn btn-primary jh-create-entity" id="jh
-create-
entity" data-cy="entityCreateButton">
<FontAwesomeIcon icon="plus" />
Add Terminal
</Link>
<hr color="red" />
<Form form={form} component={false}>
<Table
components={{
body: {
cell: EditableCell,
},
}}
columns={mergedColumns}
dataSource={modifiedData}
bordered
loading={loader}
/>
</Form>
</div>
</>
Thanks in anticipation
I cant add a button into every row of MUI DataGrid.
I have a MUI DataGrid which I render like this:
<DataGrid rows={rows} columns={columns} pageSize={5} checkboxSelection />
I have added into the columns variable 'actions' column where the button should be. rows are just a a data object I get from the props. how can I add a button into every row (for editing the row)? I have tried mapping the data array but it is not possible to add JSX button into every object of data.
You can add your custom component by overriding GridColDef.renderCell method and return whatever element you want.
The example below displays an action column that renders a single button in each row. When clicking the button, it alerts the current row data in json string:
const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 70 },
{
field: "action",
headerName: "Action",
sortable: false,
renderCell: (params) => {
const onClick = (e) => {
e.stopPropagation(); // don't select this row after clicking
const api: GridApi = params.api;
const thisRow: Record<string, GridCellValue> = {};
api
.getAllColumns()
.filter((c) => c.field !== "__check__" && !!c)
.forEach(
(c) => (thisRow[c.field] = params.getValue(params.id, c.field))
);
return alert(JSON.stringify(thisRow, null, 4));
};
return <Button onClick={onClick}>Click</Button>;
}
},
];
Just came across this.
What you need to do is include a renderCell method in your columns array.
const columns = [
{
field: 'col1',
headerName: 'Name 1',
width: 150,
disableClickEventBubbling: true,
},
{
field: 'col2',
headerName: 'Name 2',
width: 300,
disableClickEventBubbling: true,
},
{
field: 'col3',
headerName: 'Name 3',
width: 300,
disableClickEventBubbling: true,
},
{
field: 'col4',
headerName: 'Name 4',
width: 100,
disableClickEventBubbling: true,
},
{
field: 'col5',
headerName: 'Name 5',
width: 150,
***renderCell: renderSummaryDownloadButton,***
disableClickEventBubbling: true,
},
{
field: 'col6',
headerName: 'Name 6',
width: 150,
***renderCell: renderDetailsButton,***
disableClickEventBubbling: true,
},
]
In the above I am rendering a Button inside columns 5 and 6 which will appear on every populated row.
Above that you can have a function which creates and returns a Button from Material-ui.
const renderDetailsButton = (params) => {
return (
<strong>
<Button
variant="contained"
color="primary"
size="small"
style={{ marginLeft: 16 }}
onClick={() => {
parseName(params.row.col6)
}}
>
More Info
</Button>
</strong>
)
}
While #NearHuscarl's response answers the question perfectly, I'd like to post a TypeScript example:
const onClick = () => {
const api: GridApi = params.api;
const fields = api
.getAllColumns()
.map((c) => c.field)
.filter((c) => c !== "__check__" && !!c);
const thisRow: any = {};
fields.forEach((f) => {
thisRow[f] = params.getValue(params.id, f);
});
return alert(JSON.stringify(thisRow, null, 4));
};
return <Button onClick={onClick}>Click</Button>;
Also note, I changed the getValue call. (included the row id)
According to MUI v5 params.getValue method is deprecated and will be removed in the next major version, Instead, you can access the current row data from params.row.
{
field: 'action',
headerName: 'Action',
width: 180,
sortable: false,
disableClickEventBubbling: true,
renderCell: (params) => {
const onClick = (e) => {
const currentRow = params.row;
return alert(JSON.stringify(currentRow, null, 4));
};
return (
<Stack direction="row" spacing={2}>
<Button variant="outlined" color="warning" size="small" onClick={onClick}>Edit</Button>
<Button variant="outlined" color="error" size="small" onClick={onClick}>Delete</Button>
</Stack>
);
},
}
The currently top voted answer is outdated as of v5, because the new GridRowParams interface contains the actual row as a parameter, making the manual filtering from the GridApi unnecessary and unpractical.
Using this with renderCell can be as simple as
const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 70 },
{
field: "action",
headerName: "Action",
sortable: false,
renderCell: ({ row }: Partial<GridRowParams>) =>
<Button onClick={() => yourActionFunction(row)}>
Action
</Button>,
},
]
in TypeScript or
const columns = [
{ field: "id", headerName: "ID", width: 70 },
{
field: "action",
headerName: "Action",
sortable: false,
renderCell: ({ row }) =>
<Button onClick={() => yourActionFunction(row)}>
Action
</Button>,
},
]
in plain JavaScript.
I try to add delete button to my grid row, So I use cellRendererFramework and every thing is fine until state variable return undefined in my root component
I set my state variable with useEffect and every things seems to be OK and my grid shows data correctly
let [roles, SetRoles] = React.useState<Role[]>();
React.useEffect(() => {
List().then((roles) => {
console.log("set roles count:" + roles.data.length);
SetRoles(roles.data);
});
return () => {
console.error("Component will unmount!");
};
}, []);
ColDefs defination:
const columnDefs = [
{
headerName: "کلید",
field: "id",
flex: 0,
hide: true,
},
{
headerName: "عنوان",
field: "title",
flex: 2,
sortable: true,
sort: "asc",
},
{
headerName: "توضیحات جدید",
field: "description",
flex: 6,
},
{
headerName: "حذف",
field: "Id",
flex: 1,
cellRendererFramework: function (row: any) {
return (
<Button
danger
onClick={() => {
onDeleteClick(row.data);
}}
>
حذف
</Button>
);
},
},
];
and my onDeleteHandler and my PROBLEM is
const onDeleteClick = (data: any) => {
//this line works correctly and Id of my row pass to my event
console.log(data.id);
//Issue is here, my loaded state variable in Undefined, But my other function like count of roles work correctly
console.log(roles);
};
<Button
danger
onClick={() => {
onDeleteClick(row.data['id']);
}}
>
حذف
</Button>