I would like to add a key listener with 'enter' key to "onRowAdd" action from material-table on react,
Could be able to call the function outside the table props and add a normal js listener to key enter for launch the function?
So i searched on the doc of material-table and nothing similar can help me, anyone have some idea how i can fix it?
thanks
import React, { useEffect, useState } from 'react'
import MaterialTable from 'material-table'
import axios from 'axios'
var emitter = require('../config/global_emitter')
export default function MaterialTableDemo(props) {
const [state, setState] = useState({
columns: [
{ title: 'Item', field: 'item' },
{ title: 'Quantity', field: 'quantity', type: 'numeric' },
{ title: 'Description', field: 'description' },
{ title: 'Price', field: 'price', type: 'numeric' },
{
title: 'Location',
field: 'location',
lookup: { 34: 'Batman', 63: 'Back Store' },
},
],
data: []
});
useEffect(() => {
setState({...state, data:[]})
const user = {
name: props.userSelected
}
axios.post('api', user)
.then(e => {
if (e.data !== ''){
setState({...state, data: e.data})
}
})
},[props.userSelected])
const handleUpdate = (data) => {
const upload = {
items: data,
name: props.userSelected
}
axios.post('api', upload)
.then((e) => {
if (e.status === 200){
emitter.emit('confirmMessage', 'Updated list correctly')
}
})
}
return (
<MaterialTable
title={props.userSelected + '´s items ordered'}
columns={state.columns}
data={state.data}
options={{
headerStyle: {
backgroundColor: '#01579b',
color: '#FFF'
},
rowStyle: {
opacity: 1,
animationName: 'fadeInOpacity',
animationIterationCount: 1,
animationTimingFunction: 'ease-in',
animationDuration: '2s'
}
}}
onKeyDown={(ev) => {
console.log(`Pressed keyCode ${ev.key}`);
if (ev.key === 'Enter') {
// Do code here
ev.preventDefault();
}
}}
editable={{
onRowAdd: newData =>
new Promise(resolve => {
setTimeout(() => {
resolve();
addRow(newData)
}, 600);
}),
onRowUpdate: (newData, oldData) =>
new Promise(resolve => {
setTimeout(() => {
resolve();
const data = [...state.data];
data[data.indexOf(oldData)] = newData;
handleUpdate(data)
setState({ ...state, data });
}, 600);
}),
onRowDelete: oldData =>
new Promise(resolve => {
setTimeout(() => {
resolve();
const data = [...state.data];
data.splice(data.indexOf(oldData), 1);
handleUpdate(data)
setState({ ...state, data });
}, 600);
}),
}}
/>
);
}
Related
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
});
I am fetching data from the database using API and the data will be shown in a graph under 24hours of the current time. the graph will auto refresh at x minutes. I would like to have a notification based on the latest data when it exceed the threshold of the graph. the data that is retrieved from the API will be an object.
code for the graph
const TempGraph = () => {
const [data, setData] = useState([]);
useEffect(() => {
const interval = setInterval(() => asyncFetch(),5000) //5000ms = 5sec
return () => clearInterval(interval) // clear the interval everytime
}, []);
const asyncFetch = () => {
fetch('link')
.then((response) => response.json())
.then((json) => setDatajson))
.catch((error) => {
console.log('fetch data failed', error);
});
};
const config = {
data,
xField: 'time',
yField: 'value',
seriesField:'location',
xAxis: {
title: {
text: 'Hours',
}
},
yAxis:{
title:{
text: 'Temperature in °',
}
},
annotations:[
{
type:'text',
position:['min','35'],
content:'threshold',
offsetY:-3,
style:{
textBaseline:'bottom',
},
},
{
type:'line',
start:['min','35'],
end:['max','35'],
style:{
stroke:'red',
lineDash:[2,2],
},
},
],
meta: {
time: {
alias: 'hours',
},
value: {
alias: 'temperature',
max: 50,
},
},
};
return <Line {...config} />;
}
export default TempGraph;
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
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>
),
}}
When adding a row to an editable table using the library material-table, how can you customize a field? I'd like to either make the field read-only or something else where the user cannot change the field. I've tried the read-only option for columns, but that only makes it read-only for updating fields.
import React from "react";
import MaterialTable from "material-table";
import Edit from "#material-ui/icons/Edit"
import Add from "#material-ui/icons/Add"
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<MaterialTable
title="Editable Preview"
columns={[
{ title: "Name", field: "name", readonly: true }, // only works on update
{ title: "Surname", field: "surname" },
{ title: "Birth Year", field: "birthYear", type: "numeric" }
]}
data={[
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{
name: "Zerya Betül",
surname: "Baran",
birthYear: 2017,
birthCity: 34
}
]}
title="Basic"
options={{
paging: false
}}
icons={{
Add: () => <Add />,
Edit: () => <Edit />
}}
editable={{
onRowAdd: newData =>
new Promise((resolve, reject) => {
setTimeout(() => {
{
/* const data = this.state.data;
data.push(newData);
this.setState({ data }, () => resolve()); */
}
resolve();
}, 1000);
}),
onRowUpdate: (newData, oldData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
{
// const data = this.state.data;
// const index = data.indexOf(oldData);
// data[index] = newData;
// this.setState({ data }, () => resolve());
}
resolve()
}, 1000)
})
}}
/>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
According to the docs you can override the component and just use a custom function to render the field.
...
<MaterialTable
title="Editable Preview"
component={{
// add the custom component here
}}
columns={[
{ title: "Name", field: "name", readonly: true }, // only works on update
{ title: "Surname", field: "surname" },
{ title: "Birth Year", field: "birthYear", type: "numeric" }
]}
data={[
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{
name: "Zerya Betül",
surname: "Baran",
birthYear: 2017,
birthCity: 34
}
]}
title="Basic"
options={{
paging: false
}}
icons={{
Add: () => <Add />,
Edit: () => <Edit />
}}
editable={{
onRowAdd: newData =>
new Promise((resolve, reject) => {
setTimeout(() => {
{
/* const data = this.state.data;
data.push(newData);
this.setState({ data }, () => resolve()); */
}
resolve();
}, 1000);
}),
onRowUpdate: (newData, oldData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
{
// const data = this.state.data;
// const index = data.indexOf(oldData);
// data[index] = newData;
// this.setState({ data }, () => resolve());
}
resolve()
}, 1000)
})
}}
/>
);
}
...