I have created a Material UI DataGrid table and trying to make its fields editable. I am facing an issue while editing the fields using my custom logic. My custom editing logic works when I press enter however when I edit the value and then click outside of the column (somewhere else on the page) the internal value gets updated according to the logic however the displayed cell value doesn't match the updated value, it shows the user-edited value.
import React, { useEffect } from "react";
import { makeStyles, LinearProgress } from "#material-ui/core";
import {
DataGrid,
GridColDef,
GridOverlay,
GridCellEditCommitParams,
GridValueFormatterParams,
} from "#material-ui/data-grid";
const useStyles = makeStyles({
root: {
"& .super-app-theme--header": {
backgroundColor: "#f2f2f2",
},
},
muiGridRoot: {
maxHeight: "77px !important",
"& .MuiDataGrid-columnHeaderTitle": {
fontWeight: "600 !important" as any,
},
},
});
interface props {
coilId: string;
}
export default function TargetMechnicalPropsTable({
coilId,
}: props) {
const classes = useStyles();
const [rows, setRows] = React.useState<any>([
{
id: 1,
UpperLimitValue: 124.232,
lowerLimitValue: 24.232,
targetValue: 67.33,
element: "Yield Strength",
},
]);
const columns: GridColDef[] = [
{
field: "id",
hide: true,
headerClassName: "super-app-theme--header",
sortable: false,
flex: 1,
align: "center",
headerAlign: "center",
},
{
field: "element",
headerName: "Element",
headerClassName: "super-app-theme--header",
editable: false,
sortable: false,
flex: 1,
align: "center",
headerAlign: "center",
},
{
field: "targetValue",
headerName: "Target value",
headerClassName: "super-app-theme--header",
valueFormatter: (params: GridValueFormatterParams) => {
const valueFormatted = Number(params.value as number).toFixed(4);
return `${valueFormatted}`;
},
editable: true,
//type: "number",
sortable: false,
flex: 1,
align: "center",
headerAlign: "center",
},
{
field: "lowerLimitValue",
headerName: "Lower Limit",
headerClassName: "super-app-theme--header",
valueFormatter: (params: GridValueFormatterParams) => {
const valueFormatted = Number(params.value as number).toFixed(4);
return `${valueFormatted}`;
},
editable: true,
sortable: false,
flex: 1,
align: "center",
headerAlign: "center",
},
{
field: "UpperLimitValue",
headerName: "Upper Limit",
headerClassName: "super-app-theme--header",
sortable: false,
valueFormatter: (params: GridValueFormatterParams) => {
const valueFormatted = Number(params.value as number).toFixed(4);
return `${valueFormatted}`;
},
editable: true,
flex: 1,
align: "center",
headerAlign: "center",
},
];
const handleCellEditCommit = React.useCallback(
({ id, field, value }: GridCellEditCommitParams) => {
const updatedRows = rows.map((row: any) => {
if (row.id === id) {
switch (field) {
case "lowerLimitValue":
if (
Number(value) < Number(row.targetValue) &&
Number(value) < Number(row.UpperLimitValue)
) {
return { ...row, lowerLimitValue: Number(value) };
} else {
return {
...row,
lowerLimitValue: Number(row.lowerLimitValue),
};
}
case "UpperLimitValue":
if (
Number(value) > Number(row.targetValue) &&
Number(value) > Number(row.lowerLimitValue)
) {
return { ...row, UpperLimitValue: Number(value) };
} else {
return {
...row,
UpperLimitValue: Number(row.UpperLimitValue),
};
}
case "targetValue":
if (
Number(value) > Number(row.lowerLimitValue) &&
Number(value) < Number(row.UpperLimitValue)
) {
return { ...row, targetValue: Number(value) };
} else {
return {
...row,
targetValue: Number(row.targetValue),
};
}
}
}
return row;
});
setRows(updatedRows);
},
[rows]
);
return (
<div style={{ width: "100%" }} className={classes.root}>
<DataGrid
classes={{
root: classes.muiGridRoot,
}}
rows={rows}
columns={columns}
hideFooterPagination
hideFooter
disableColumnMenu
disableColumnFilter
disableColumnSelector
autoHeight
disableExtendRowFullWidth
density="compact"
onCellEditCommit={handleCellEditCommit}
/>
</div>
);
}
Please let me know what needs to be done for my custom logic to work when the user clicks outside of the column while editing it so that the user displayed value matches with the internal logic value.
Thank you!
Related
I have a Data Grid component of the MUI and I am fetching data using axios, of course the objective is to show the data fetched in the Data Grid component, when I looked in the console it returns me correct, but in the page it returns me only one result o duplicating.
I believe there is a problem with the front-end but I am not able to identify it.
JSON
//Code
export default function ListGrid() {
const [load, setLoad] = useState(false);
const [users, setUsers] = useState<IDados[]>([]);
interface IDados {
empresa: number,
nome: string,
matricula: number,
ano: number,
}
useEffect(() => {
setLoad(true);
if (load === true) {
axios
.get("http://localhost:8080/questionarios/all", {
headers: {
...getTokenAuth()
},
})
.then((res) => {
setUsers(res.data);
// console.log(res.data);
})
}
}, [load]);
const columns: GridColDef[] = [
{
field: 'empresa',
headerName: 'Empresa',
width: 120,
headerAlign: 'center',
align: 'center',
},
{
field: 'nome',
headerName: 'Nome',
width: 250,
headerAlign: 'center',
align: 'center',
valueGetter: (params) => {
return params.getValue(params.id, "colaborador").nome;
}
},
{
field: 'matricula',
headerName: 'Matrícula',
width: 150,
headerAlign: 'center',
align: 'center',
},
{
field: 'ano',
headerName: 'Ano',
width: 150,
headerAlign: 'center',
align: 'center',
},
];
return (
<Box sx={{ height: 400, width: '100%' }}>
<DataGrid
rows={users}
columns={columns}
checkboxSelection
disableSelectionOnClick
experimentalFeatures={{ newEditingApi: true }}
localeText={ptBR.components.MuiDataGrid.defaultProps.localeText}
pageSize={pageSize}
onPageSizeChange={(newPageSize) => setPageSize(newPageSize)}
rowsPerPageOptions={[5, 10, 20]}
pagination
initialState={{
filter: {
filterModel: {
items: [],
quickFilterLogicOperator: GridLinkOperator.Or,
},
},
}}
components={{ Toolbar: barraPesquisa }}
getRowId={(row: any) => row.name + row.matricula}
/>
</Box>
);
}
Your warning message seems to indicate some keys are NaN. Maybe you have a problem with
getRowId={(row: any) => row.name + row.matricula}
which return NaN for some rows
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 have a list item callled Row. The Row has three sections, I have stored the width of each sections in a state
rowWidth: [
{
id: 'name',
width: 200,
},
{
id: 'size',
width: 70,
},
{
id: 'status',
width: 150,
},
{
id: 'progress',
width: 200,
},
{
id: 'lasttry',
width: 200,
},
{
id: 'eta',
width: 50,
},
]
I change width of a single column by an external handle which updates the store.
switch (actions.type) {
case consts.CHANGE_HEADER_WIDTH : return {
...state,
headerWidth: state.headerWidth.map((item) =>
item.id === actions.payload.id ? { ...item, width: actions.payload.neWidth } : item
),
}
break;
default : return state;
}
This is the row
const headerWidth = useSelector((state) => state.ui.headerWidth)
const getWidth = useCallback((id) => {
const l = _.find(headerWidth, function (item) {
return item.id === id
})
return l.width
})
return(
<Row>
<Section1 style={{ width: getRow('name') }}/>
<Section2 style={{ width: getRow('size') }}/>
<Section3 style={{ width: getRow('status') }}/>
</Row>
)
My question is - how do I change the width of these divs without rerendering row
Thank you all for the help. I understand that there has to be a rerender in order to view the state change.
I'm having issue when rendering dynamic columns for Antd table, when I re-render the columns, the table's header just disappear.
getDefaultColumns()
const getDefaultColumns = () => {
return [
{
title: <span className={`table-header`}>STT</span>,
align: "center",
key: "STT",
render: (value, record, index) =>
(currentPage - 1) * pageSize + index + 1,
width: 50,
fixed: "left",
},
{
title: <span className={`table-header pl-4`}>Name</span>,
dataIndex: "full_name",
width: 150,
fixed: "left",
align: "left",
render: (text, record) => (
<span className="pl-4">{record.full_name}</span>
),
},
{
title: <span className={`table-header pl-4`}>Code</span>,
dataIndex: "code",
fixed: "left",
align: "left",
render: (text, record) => <span className="pl-4">{record.code}</span>,
},
{
title: <span className={`table-header`}>DOB</span>,
dataIndex: "birthday",
align: "center",
render: (value) => <span>{moment(value).format(DATE_FORMAT)}</span>,
fixed: "left",
},
{
title: <span className={`table-header`}>SUMMARY</span>,
children: [
{
title: (
<span className={`table-header`}>
TB{" "}
{semester.key !== "whole_year"
? `HK ${semester.shortValue}`
: semester.value}
</span>
),
dataIndex: ["study_summary_info", "average_semester"],
key: "study_summary_info_average_semester",
align: "center",
},
{
title: <span className={`table-header`}>Rank</span>,
key: "study_summary_info_ranked_academic",
align: "center",
render: (value, record) => {
return (
<SelectOption
...
/>
);
},
},
{
title: <span className={`table-header`}>Conduct</span>,
key: "study_summary_info_conduct",
align: "center",
render: (value, record) => {
return (
<SelectOption
...
/>
);
},
},
],
},
{
title: <span className={`table-header`}>Day off</span>,
dataIndex: ["study_summary_info", "day_off"],
key: "study_summary_info_day_off",
align: "center",
},
{
title: <span className={`table-header`}>Title</span>,
key: "study_summary_info_title",
align: "center",
render: (value, record) => {
return (
<SelectOption
...
/>
);
},
},
{
title: <span className={`table-header`}>Result</span>,
key: "study_summary_info_result",
align: "center",
render: (value, record) => {
return (
<SelectOption
...
/>
);
},
},
];
};
setupColumns()
const setupColumns = () => {
filterClassHasSubject({ class_id: classId, page: 1, size: 999 })
.then((res) => {
if (res.data.code === "success") {
const data = res.data.data;
let children = data.map((val) => {
return {
title: (
<span className={`table-header`}>{val.subject_info?.name}</span>
),
key: (value, record, index) => `subject_${index}`,
width: 150,
align: "center",
ellipsis: true,
render: (value, record) => {
const point = record.average_transcript_info.find(
(x) =>
x.subject_id === val.subject_id &&
x.semester === semester.key
);
return (
<span>{point?.value_number}</span>
);
},
};
});
let scoreGroup = {
title: <span className={`table-header`}>DTB</span>,
children,
};
let temp = getDefaultColumns();
temp.splice(4, 0, scoreGroup);
setColumns([...temp]);
}
});
};
Table
<Table
dataSource={summaryData}
columns={columns}
bordered
rowClassName={(record, index) =>
index % 2 === 1
? "odd-row attendance-row"
: "bg-white attendance-row"
}
pagination={false}
scroll={{ x: "100vw", y: 580 }}
/>
This is not happen when I remove the scroll Y on the Table.
The columns is render dynamically when semester or classId is changed.
Before change semester
After change semester
In my case, it was because of the property key in the column. The solution was to use string instead of using the function to return string.
https://ant.design/components/table/
I want to convert numbers to money using Table antd In line with this example I'm so confused, please help me, I hurry because I have to submit the last project before I graduate.
const timeConverter = rawDate => moment(rawDate).tz("Thai/Bangkok").format('L');
const { pagination } = this.props;
const columns = [
{
title: "ID",
dataIndex: "id",
align: "center",
width: "60px",
height: "30px",
},
{
title:"วันที่บันทึก",
dataIndex:"createdAt",
align:"center",
width:"200",
format:"0,0",
render: createdAt => timeConverter(createdAt)
},
{
title:"อ้างอิงใบเสร็จเลขที่ ",
dataIndex:"re_reference",
align:"center",
width:"200"
},
{
title:"ชื่อบริการ/สินค้า",
dataIndex:"re_pro_name",
align:"center",
width:"350",
},
{
title:"รายละเอียด",
dataIndex:"re_detail",
align:"center",
width:"100",
},
{
title:"รหัสสมาชิก (ถ้ามี)",
dataIndex:"re_cus_name",
align:"center",
width:"200",
},
{
title:"ประเภทรับ-จ่าย",
dataIndex:"re_type",
align:"center",
width:"350",
},
{
title:"ราคาต่อหน่วย",
dataIndex:"re_price",
align:"center",
width:"300",
// render: re_price => numeral('$0,0.00')(re_price)
render:(text, record) => (
<span> </span>
)
},
render: value => ${value}.00.replace(/\B(?=(\d{3})+(?!\d))/g, ',') //convert number monney