conditional editable on cellEdit in material-table - reactjs

when i use editable attribute in schema
editable: (row, rowData) => {
return rowData.town === "scaraborough";
},
it works fine but when i use celEdit editable property of field just working with boolean
const tableRef = React.useRef();
const [columns, setColumns] = useState<Column<schema>[]>([
{
title: "Name",
field: "name",
editable: "always",
type: "string",
resizable: true,
emptyValue: <div style={{ visibility: "hidden" }}>empty</div>,
},
{
title: "Town",
field: "town",
editable: (row, rowData) => {
return rowData.town === "scaraborough";
},
type: "string",
},
{
title: "Digits",
field: "digits",
type: "numeric",
lookup: { 63: "212", 212: "1212" },
},
{ title: "status", field: "status", editable: "always", type: "boolean" },
]);
const [data, setData] = useState<schema[]>([
{
name: "",
town: "sample input data",
digits: 63,
status: false,
},
{ name: "jimmy", town: "scaraborough", digits: 63, status: false },
{ name: "sdsdd", town: "china", digits: 212, status: false },
]);
const options = { filtering: true, selection: true };
return (
<MaterialTable
tableRef={tableRef}
columns={columns}
data={data}
options={options}
// editable={{
// onRowUpdate: (newData, oldData) =>
// new Promise((resolve, reject) => {
// const dataUpdate = [...data];
// resolve(data);
// }),
// }}
cellEditable={{
onCellEditApproved: (newValue, oldValue, rowData: any, columnDef) => {
return new Promise((resolve, reject) => {
resolve();
});
},
}}
/>
);
"material-table": "^1.69.3",

Related

MUI datagrid view mode not persisting

I'm implementing a MUI datagrid with inline editing. Whenever a cell loses focus, the code sets the mode to 'view' (as it should) but then immediately the processRowUpdate callback is called (to send data to the endpoint) it sets the cell mode back to 'edit' meaning that the cell remains in edit mode.
Does anyone know why this is happening?
Maybe something to do with this processRowUpdate error logged to console?:
TypeError: Cannot read properties of undefined (reading 'id')
at getRowId (productTable.js:213:1)
at getRowIdFromRowModel (gridRowsUtils.js:17:1)
at useGridRows.js:106:1
at Array.forEach (<anonymous>)
at Object.updateRows (useGridRows.js:105:1)
at apiRef.current.<computed> [as updateRows] (useGridApiMethod.js:14:1)
at useGridCellEditing.new.js:334:1
Code:
export default function FullFeaturedCrudGrid(props) {
const [rows, setRows] = React.useState([]);
const [cellModesModel, setCellModesModel] = React.useState({})
const [selectedCellParams, setSelectedCellParams] = React.useState(null);
const { tableName } = props
const [snackbar, setSnackbar] = React.useState(null);
const handleCloseSnackbar = () => setSnackbar(null);
React.useEffect(() => {
console.log('useEffect called')
axios.get(`http://localhost:8000/mvhr/all`)
.then((response) => {
setRows(response.data);
})
}, [])
React.useEffect(() => {
console.log('cellModesModel',cellModesModel)
});
const handleCellFocus = React.useCallback((event) => {
const row = event.currentTarget.parentElement;
const id = row.dataset.id;
const field = event.currentTarget.dataset.field;
setSelectedCellParams({ id, field });
}, []);
const handleDeleteClick = (id) => () => {
axios.delete(`http://localhost:8000/delete_mvhr/${id}`
).then(() => {
setRows(rows.filter((row) => row.id !== id));
setSnackbar({ children: tableName + ' successfully deleted', severity: 'success' });
})
};
const handleCancel = () => {
if (!selectedCellParams) {
return;
}
const { id, field } = selectedCellParams;
setCellModesModel({
...cellModesModel,
[id]: {
...cellModesModel[id],
[field]: { mode: GridCellModes.View },
},
});
};
const processRowUpdate = React.useCallback(
(newRow) => {
axios.put(`http://localhost:8000/mvhr/`, newRow)
.then((response) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
setSnackbar({ children: tableName + ' successfully saved', severity: 'success' });
return updatedRow
})
});
const handleProcessRowUpdateError = React.useCallback((error) => {
setSnackbar({ children: error.message, severity: 'error' });
}, []);
const columns = [
{ field: 'description', headerName: 'description', width: 180, editable: true },
{ field: 'elec_efficiency', headerName: 'elec_efficiency', type: 'number', editable: true },
{ field: 'heat_recovery_eff', headerName: 'heat_recovery_eff', type: 'number', editable: true },
{ field: 'range_low', headerName: 'range_low', type: 'number', editable: true },
{ field: 'range_high', headerName: 'range_high', type: 'number', editable: true },
{ field: 'superseded', headerName: 'superseded', type: 'boolean', editable: true },
{
field: 'actions',
type: 'actions',
headerName: 'Actions',
width: 100,
cellClassName: 'actions',
getActions: ({ id }) => {
return [
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
onClick={handleDeleteClick(id)}
color="inherit"
/>,
];
},
},
];
return (
<Box
sx={{
height: '100vh',
width: '100%',
'& .actions': {
color: 'text.secondary',
},
'& .textPrimary': {
color: 'text.primary',
},
}}
>
<StripedDataGrid
rows={rows}
columns={columns}
processRowUpdate={processRowUpdate}
onProcessRowUpdateError={handleProcessRowUpdateError}
onCellEditStop={handleCancel}
cellModesModel={cellModesModel}
onCellModesModelChange={(model) => setCellModesModel(model)}
components={{
Toolbar: AddToolbar,
}}
componentsProps={{
toolbar: { setRows, setSnackbar, tableName },
cell: {
onFocus: handleCellFocus,
},
}}
experimentalFeatures={{ newEditingApi: true }}
getRowClassName={(params) =>
params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
}
/>
{!!snackbar && (
<Snackbar
open
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
onClose={handleCloseSnackbar}
autoHideDuration={6000}
>
<Alert {...snackbar} onClose={handleCloseSnackbar} />
</Snackbar>
)}
</Box>
);
}
Solved it. I refactored the code to return the updatedRow synchronously rather than as a callback to the axios request:
const processRowUpdate = React.useCallback(
(newRow) => {
const updatedRow = { ...newRow, isNew: false };
setRows(rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
setSnackbar({ children: tableName + ' successfully saved', severity: 'success' });
axios.put(`http://localhost:8000/mvhr/`, newRow)
return updatedRow
});

Data is not showing in Grid Lookup React.js MaterialUI

I need to show my lookup like this
Lookup image what I want
So the code for this lookup is which is shown is hardcoded but i want to do retrieve data dynamically.
Hardcoded data
I had done with to retrieve data from database and the data is also in same format when i console log the both they are same.
AvaibleMechanic is which we retrieve from DB and dyanmicMechanicLookUp is harcoded
Both data console log SS
setColumnsCode
const [columns, setColumns] = useState([
{ title: "OrderId", field: "_id", editable: "never" },
{ title: "Customer Name", field: "customerName", editable: "never" },
{ title: "Car Name", field: "carName", editable: "never" },
{ title: "Car Number", field: "carNumber", editable: "never" },
{ title: "Address", field: "custAddress", editable: "never" },
{ title: "Service Name", field: "serviceName", editable: "never" },
{ title: "Price", field: "servicePrice", editable: "never" },
{
title: "Assign Mechanic",
field: "mechanicId",
lookup: AvailableMechanic,
},
]);
when i want to see data which is retrieve from Database is not show on the lookup is look like this
Retrieve from database lookup SS
You can see when I used dynamicLookUp which is hardcoded it works fine but when i used data which is retrieve it's not shown on the grid
FullCode
import React, { useState, useEffect } from "react";
import AdminOrders from "../../../services/member/orders.js/admin_orders";
import MechanicService from "../../../services/member/Mechanic/Mechanic_Services";
import "./CSS/Cars.css";
import MaterialTable from "material-table";
import { useSnackbar } from "notistack";
function Orders() {
const [orders, setOrders] = useState([]);
const [completedOrders, setCompletedOrders] = useState([]);
const [AvailableMechanic, setAvailableMechanic] = useState({});
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
//for error handling
const [iserror, setIserror] = useState(false);
const [errorMessages, setErrorMessages] = useState([]);
const getAvailableMechanics = async () => {
await MechanicService.findAvailable()
.then((res) => {
res.map((key,i) => {
setAvailableMechanic(prevState => ({
...prevState,
[key._id]: key.name
}))
})
})
.catch((err) => {
console.log(err)
})
}
const getPlacedOrders = () => {
AdminOrders.findPlacedOrders()
.then((response) => {
setOrders(response);
})
.catch((err) => {
console.log(err);
});
};
const getCompletedOrders = () => {
AdminOrders.findCompletedOrders()
.then((res) => {
setCompletedOrders(res);
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
getAvailableMechanics();
getPlacedOrders();
getCompletedOrders();
}, []);
const dynamicMechanicsLookUp = {
'621d06355e364ae391f59af4': "John Doe 1",
'621e3fb9d99ae5efd754b5d6': "John Doe 2",
'62766409db9ad573da5655cc': "John Doe 3",
'6276642cdb9ad573da5655d1': "John Doe 4",
'62766433db9ad573da5655d4': "John Doe 5",
};
// const dynamicMechanicsLookUp = AvailableMechanic;
console.log("dynamicLookUp",dynamicMechanicsLookUp)
console.log("AvailableMechanic",AvailableMechanic)
const [columns, setColumns] = useState([
{ title: "OrderId", field: "_id", editable: "never" },
{ title: "Customer Name", field: "customerName", editable: "never" },
{ title: "Car Name", field: "carName", editable: "never" },
{ title: "Car Number", field: "carNumber", editable: "never" },
{ title: "Address", field: "custAddress", editable: "never" },
{ title: "Service Name", field: "serviceName", editable: "never" },
{ title: "Price", field: "servicePrice", editable: "never" },
{
title: "Assign Mechanic",
field: "mechanicId",
lookup: AvailableMechanic,
},
]);
const [column, setColumn] = useState([
{ title: "OrderId", field: "_id" },
{ title: "Customer Name", field: "customerName" },
{ title: "Car Name", field: "carName" },
{ title: "Car Number", field: "carNumber" },
{ title: "Address", field: "custAddress" },
{ title: "Service Name", field: "serviceName" },
{ title: "Price", field: "servicePrice" },
{ title: "Assigned Mechanic", field: "mechanicId" },
]);
const handleRowUpdate = (newData, oldData, resolve) => {
let errorList = [];
if (errorList.length < 1) {
AdminOrders.assignOrder(newData._id, newData.mechanicId)
.then((res) => {
const dataUpdate = [...orders];
const index = oldData.tableData.id;
dataUpdate[index] = newData;
setOrders([...dataUpdate]);
resolve();
setIserror(false);
setErrorMessages([]);
enqueueSnackbar(res, {
variant: "success",
});
})
.catch((error) => {
setErrorMessages(["Update failed! Server error"]);
setIserror(true);
resolve();
});
} else {
setErrorMessages(errorList);
setIserror(true);
resolve();
}
};
const [display, setdisplay] = useState(false);
const openTable = () => {
setdisplay(true);
};
const closeTable = () => {
setdisplay(false);
};
return (
<div className="cars_container">
<br />
<button onClick={openTable}>See Completed Orders</button>
<br />
{orders ? (
<MaterialTable
title="CURRENT ORDERS DATA"
columns={columns}
data={orders}
editable={{
onRowUpdate: (newData, oldData) =>
new Promise((resolve, reject) => {
handleRowUpdate(newData, oldData, resolve);
}),
}}
options={{
headerStyle: {
backgroundColor: "#01579b",
color: "#FFF",
},
exportButton: true,
}}
/>
) : (
<div>
<br />
<h2>NO CURRENT ORDERS RIGHT NOW</h2>
</div>
)}
<br />
<br />
<br />
{display ? (
<div>
<h1>COMPLETED ORDERS</h1>
<MaterialTable
title="CURRENT ORDERS DATA"
columns={column}
data={completedOrders}
options={{
headerStyle: {
backgroundColor: "#01579b",
color: "#FFF",
},
exportButton: true,
}}
/>
<br />
<button onClick={closeTable}>Close Table</button>
<br />
<br />
<br />
</div>
) : null}
</div>
);
}
export default Orders;
Sorry for the bad english. Please if you find any mistake i am making please let me know
Thanks

How to add button to material ui table at the bottom?

This is the code for the material ui in react typescript. I am trying to add a button at the bottom which when clicked leads to a form, but after trying many things, I still dont know how to do that?
I just need a simple button at the bottom, any help is appreciated
This is the code from the website
import React from 'react';
import MaterialTable, { Column } from 'material-table';
interface Row {
name: string;
surname: string;
birthYear: number;
birthCity: number;
}
interface TableState {
columns: Array<Column<Row>>;
data: Row[];
}
export default function MaterialTableDemo() {
const [state, setState] = React.useState<TableState>({
columns: [
{ title: 'Name', field: 'name' },
{ title: 'Surname', field: 'surname' },
{ title: 'Birth Year', field: 'birthYear', type: 'numeric' },
{
title: 'Birth Place',
field: 'birthCity',
lookup: { 34: 'İstanbul', 63: 'Şanlıurfa' },
},
],
data: [
{ name: 'Mehmet', surname: 'Baran', birthYear: 1987, birthCity: 63 },
{
name: 'Zerya Betül',
surname: 'Baran',
birthYear: 2017,
birthCity: 34,
},
],
});
return (
<MaterialTable
title="Editable Example"
columns={state.columns}
data={state.data}
editable={{
onRowAdd: (newData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
setState((prevState) => {
const data = [...prevState.data];
data.push(newData);
return { ...prevState, data };
});
}, 600);
}),
onRowUpdate: (newData, oldData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
if (oldData) {
setState((prevState) => {
const data = [...prevState.data];
data[data.indexOf(oldData)] = newData;
return { ...prevState, data };
});
}
}, 600);
}),
onRowDelete: (oldData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
setState((prevState) => {
const data = [...prevState.data];
data.splice(data.indexOf(oldData), 1);
return { ...prevState, data };
});
}, 600);
}),
}}
/>
);
}
This is what i want the button to look like:
You can have a component that returns both your MaterialTableDemo and the button.
You can wrap both of them in a div, or use the React.Fragment to inline them.
function TableWithButton() {
return (
<>
<MaterialTableDemo />
<div style={{ width: '100%', textAlign: 'center'}}>
<Button onClick={navigateToForm}>Button</Button>
</div>
</>
);
}
Here is an example

Material-Table React. How to make the table Title and Header sticky

Is it possible to make the table title, search field, global actions icons and column headers in the Material-Table sticky?
I've tried adding headerStyle to options and that has no effect (anyway that would only affect column headers and not the table title etc)
options={{
headerStyle: { position: 'sticky'},
paging: false,
search: false,
}}
Has anyone got any ideas how to do it?
I was hoping a 'sticky header' option existed but if it does I cannot see it!
I would have thought a sticky header is a fairly common use case for tables.
This is the basic code to use a Material Table:
import React from 'react';
import MaterialTable from 'material-table';
export default function MaterialTableDemo() {
const [state, setState] = React.useState({
columns: [
{ title: 'Name', field: 'name' },
{ title: 'Surname', field: 'surname' },
{ title: 'Birth Year', field: 'birthYear', type: 'numeric' },
{
title: 'Birth Place',
field: 'birthCity',
lookup: { 34: 'İstanbul', 63: 'Şanlıurfa' },
},
],
data: [
{ name: 'Mehmet', surname: 'Baran', birthYear: 1987,
birthCity: 63 },
{
name: 'Zerya Betül',
surname: 'Baran',
birthYear: 2017,
birthCity: 34,
},
],
});
return (
<MaterialTable
title="Editable Example"
columns={state.columns}
data={state.data}
editable={{
onRowAdd: (newData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
setState((prevState) => {
const data = [...prevState.data];
data.push(newData);
return { ...prevState, data };
});
}, 600);
}),
onRowUpdate: (newData, oldData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
if (oldData) {
setState((prevState) => {
const data = [...prevState.data];
data[data.indexOf(oldData)] = newData;
return { ...prevState, data };
});
}
}, 600);
}),
onRowDelete: (oldData) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
setState((prevState) => {
const data = [...prevState.data];
data.splice(data.indexOf(oldData), 1);
return { ...prevState, data };
});
}, 600);
}),
}}
/>
);
}
`
I figured it out in the end:
I had to add the these to the Material Table options. It means knowing in advance the height that you want your table to be. I
options={{
headerStyle: { position: 'sticky', top: 0 },
maxBodyHeight: 500,
}}
and then also this was necessary to add to the Material Table depending on pagination setting:
components={{
Container: props => (
<div style={{height: 500}}>
{props.children}
</div>
),
}}

How to do onclick on particular table row in MaterialTable

I have two issues in MaterialTable
* I am using MaterialTable in reactjs , i want to do onClick on particular row . I am passing JSON data in table. How can i implement this?
columns: [
{ title: 'Username', field: 'username' },
{ title: 'Team Membership', field: 'teammembership' },
],
<MaterialTable
title = "Team Members"
columns={this.state.columns}
data={this.state.rowData}
/>
teammembership should be clickable
*How to add a button in particular row, rather than action? I am already using action in first row , i want to add one button not along with action, but in 3rd row.
columns: [
{ title: 'User Name', field: 'username' },
{ title: 'Role',
field: 'roles',
lookup: { 34: 'Primary', 63: 'Secondary' ,53 : 'Escallation', 54:'Override ' },
},
{ title: 'Start Date', field: 'Startdate', type: 'datetime' },
{ title: 'End Date', field: 'enddate', type: 'datetime' },
{title : 'Repeat', field:'repeat'},
],
data: [
{ username: 'Mehmet', roles: '34', Startdate: 1987, enddate: 2018,repeat:'repeat' },
{
username: 'Zerya Betül',
roles: '63',
Startdate: 2017,
enddate: 2019,
repeat:'repeat'
},
],
<MaterialTable
title = ""
columns={this.state.columns}
data={ this.state.data}
editable={{
onRowAdd: newData =>
new Promise(resolve => {
setTimeout(() => {
resolve();
const data = [...this.state.data];
data.push(newData);
setState({ ...this.state, data });
}, 600);
}),
onRowUpdate: (newData, oldData) =>
new Promise(resolve => {
setTimeout(() => {
resolve();
const data = [...this.state.data];
data[data.indexOf(oldData)] = newData;
setState({ ...this.state, data });
}, 600);
}),
onRowDelete: oldData =>
new Promise(resolve => {
setTimeout(() => {
resolve();
const data = [...this.state.data];
data.splice(data.indexOf(oldData), 1);
setState({ ...this.state, data });
}, 600);
}),
}}
/>
in the place of repeat i want to place a button
Any help would be appreciated. Thank you.
The component has an onRowClick prop which can be used to have things happen onClick of a given row. Please see the documentation here and look specifically at the section "Detail Panel With RowClick Example".

Resources