Prevent user from entering negative number validation material-table - reactjs

How can I prevent the user from adding a negative number to my table? I thought that having the type: numeric would do the trick but I still can enter a negative number into it.
Could I create a function and added to my promise?
<MaterialTable
aria-label="List of pseudopotentials"
icons={tableIcons}
title="Pseudopotentials"
columns={[
{title: 'Element', field: 'kind', editable: 'never'},
{title: 'Atomic Mass (AMU)', field: 'atomic_mass', type: 'numeric'}, <----- TYPE NUMERIC HERE
{title: 'Filename', field: 'filename', editable: 'never'},
]}
data={createTable(selectedPseudos, customAtomicMasses)}
options={{
paging: false,
rowStyle: (x: {tableData: {id: number}}) => {
if (x.tableData.id % 2) {
return {backgroundColor: '#ddd'}
}
},
maxBodyHeight: '25em',
headerStyle: {
backgroundColor: '#ddd',
padding: '0.25em 1em',
fontWeight: 'bolder',
},
}}
editable={{
/** NEED TO PREVENT NEGATIVE NUMBER*/
onRowUpdate: (newData) =>
new Promise((resolve) => {
const newMasses = {...customAtomicMasses}
newMasses[newData.kind] = newData.atomic_mass
setCustomAtomicMasses(newMasses)
resolve(0)
}),
}}
actions={[
{
icon: () => {
return <Search aria-label="search-pseudopotentials" />
},
tooltip: 'Search pseudopotentials',
onClick: (_event, rowData) => {
setModalKind(rowData.kind)
setShowModal(true)
},
},
]}
></MaterialTable>

Related

Rows Duplicates MUI DataGrid

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

React material-ui datagrid editing Issue

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!

Execute function when I apply filter, material-table

I am working with Material_table, what I am looking for is that when my user applies a filter to send a method
I want to call this method
const subscribeRemoteRequestsArticle = (id) => {
subscriptionRemoteRequests = API.graphql(
.....
.....
}
Table
<MaterialTable
icons={tableIcons}
columns={[
{
field: "clacte",
title: props.translate("Key"),
/* customFilterAndSearch: (term, rowData) => subscribeRemoteRequestsArticle <-- here perform the test */
},
{
field: "nomcte",
title: props.translate("Customers"),
},
{
field: "nomemp",
title: props.translate("Company Key"),
},
{
field: "nomregion",
title: props.translate("Name region"),
},
{
field: "nomgirocom",
title: props.translate("Commercial Business"),
/* lookup: "NomGiroCom", */
},
]}
data={customerList}
title={props.translate("List of customers")} //
options={{
selection: true,
filtering: true,
search: false,
searchable: false,
selectionProps: rowData => ({
disabled: rowData.name === 'Mehmet',
color: 'primary',
})
}}
onSelectionChange={SelectRows}
localization={localization(props)}
/>
Someone who can tell me how to do it, or where I can find documentation to answer my question?
Pass your custom function to the prop columns.
<MaterialTable
columns={[
{
title: 'Clave',
field: 'clave',
customFilterAndSearch: (term, rowData) => subscribeRemoteRequestsArticle // here
}
]}
options={{
filtering: true
}}
/>

How to set material-table dynamic columns in react / typescript

I have a table that has to display an arbitrary number of columns which are dynamically generated from the received data.
I have created a column interface that will hold all the properties I need for each column, such as whether the column can be minimized (made narrow), whether it is a column for which I have to render an icon from a structure or maybe render specific data for the group-row, etc.
In material-table I am trying to map these columns, but always get the error:
Error: this.tableContainerDiv.current is null
This happens even if the list of objects are exactly the same as when I hard-code the columns.
My (cut-down) code:
interface IColumnObject {
field: string;
title: string;
sorting: boolean;
minimizing: boolean;
minimized: boolean;
hidden: boolean;
width: string;
grouprender: boolean;
iconrender: boolean;
}
interface State {
detailData: ITableData[];
filteredData: ITableData[];
headers: IHeaderObject[];
columns: IColumnObject[];
filterList: IFilterList;
anchorEl: Element;
csvHeader: ICsvHeader[];
csvData: ICsvData[];
}
class DetailAllRoute extends React.Component<Props, State> {
// eslint-disable-next-line #typescript-eslint/no-explicit-any
private csvLink: React.RefObject<any> = React.createRef();
...
public generateTableDetailData = () => {
const tableData: ITableData[] = [];
let createHeader = true;
let createColumn = true;
const headers: IHeaderObject[] = [];
const columns: IColumnObject[] = [];
...
// Add the Application, Filename and Location column objects to the columns list
const applicationColumn: IColumnObject = {
field: 'application',
title: this.i18n.translateToString('Column_Application'),
sorting: true,
minimizing: false,
minimized: false,
hidden: false,
width: '100px',
grouprender: false,
iconrender: false,
};
columns.push(applicationColumn);
const filenameColumn: IColumnObject = {
field: 'filename',
title: this.i18n.translateToString('Column_Filename'),
sorting: true,
minimizing: false,
minimized: false,
hidden: false,
width: '270px',
grouprender: true,
iconrender: false,
};
columns.push(filenameColumn);
const locationColumn: IColumnObject = {
field: 'location',
title: this.i18n.translateToString('Column_Location'),
sorting: true,
minimizing: true,
minimized: false,
hidden: false,
width: '350px',
grouprender: false,
iconrender: false,
};
columns.push(locationColumn);
...
}
render() {
const { classes } = this.props;
const {
compData,
filteredData,
columns,
filterList,
anchorEl,
} = this.state;
return (
<React.Fragment>
<Paper className={classes.muiListRoot} style={{ backgroundColor: '#fff' }}>
<MaterialTable
title={
<span style={{ fontSize: '2.0em', fontWeight: 'bold', color: '#19768B' }}>
{`${this.i18n.translateToString('Table_DetailData')} Test`}
</span>
}
actions={[
{
icon: FilterList,
tooltip: 'Filter',
position: 'toolbar',
onClick: event => {
this.handlePopoverClick(event);
},
},
]}
components={{
// eslint-disable-next-line react/display-name
Header: headerprops => (
<React.Fragment>
{this.getTableHeader()}
<MTableHeader {...headerprops} />
</React.Fragment>
),
}}
columns={columns.map(c => {
return {
title: c.title,
field: c.field,
sorting: c.sorting,
width: c.width,
hidden: c.hidden,
} as Column<any>;
})}
data={filteredData}
parentChildData={(row, rows) => rows.find(a => a.application === row.parent)}
options={{
toolbar: true,
sorting: true,
exportButton: { csv: true },
exportCsv: () => {
this.customExportCSV();
},
headerStyle: {
backgroundColor: '#19768B',
color: 'white',
borderBottom: '1px solid black',
},
// eslint-disable-next-line #typescript-eslint/no-explicit-any
rowStyle: (_data: any, index: number) => {
return index % 2 ? { backgroundColor: '#ecf2f9' } : {};
},
}}
localization={{
pagination: {
labelDisplayedRows: this.i18n.translateToString('String_RowFromToCount'),
labelRowsSelect: this.i18n.translateToString('String_Rows'),
labelRowsPerPage: this.i18n.translateToString('String_RowsPerPage'),
firstAriaLabel: this.i18n.translateToString('String_FirstPage'),
firstTooltip: this.i18n.translateToString('String_FirstPage'),
previousAriaLabel: this.i18n.translateToString('String_PrevPage'),
previousTooltip: this.i18n.translateToString('String_PrevPage'),
nextAriaLabel: this.i18n.translateToString('String_NextPage'),
nextTooltip: this.i18n.translateToString('String_NextPage'),
lastAriaLabel: this.i18n.translateToString('String_LastPage'),
lastTooltip: this.i18n.translateToString('String_LastPage'),
},
toolbar: {
nRowsSelected: this.i18n.translateToString('String_RowsSelected'),
searchTooltip: this.i18n.translateToString('String_Search'),
searchPlaceholder: this.i18n.translateToString('String_Search'),
exportTitle: this.i18n.translateToString('String_Export'),
exportCSVName: this.i18n.translateToString('String_ExportAs'),
},
body: {
emptyDataSourceMessage: this.i18n.translateToString('String_NoData'),
},
}}
/>
</Paper>
<div>
<CSVLink
data={csvData}
headers={csvHeader}
filename="CSV_File.csv"
ref={this.csvLink}
target="_blank"
/>
</div>
</React.Fragment>
);
}
}
Any idea what I am doing wrong in the mapping? Or does material-table not like it when one use map to generate the column-array?
When I hard-code the columns with:
...
columns={[
{
title: this.i18n.translateToString('Column_Application'),
field: 'application',
sorting: true,
width: '100px',
hidden: true,
},
{
title: this.i18n.translateToString('Column_Filename'),
field: 'filename',
sorting: true,
width: '270px',
// eslint-disable-next-line react/display-name
render: rowData =>
!!!rowData.filename ? (
<span>
<b>{this.i18n.translateToString('Column_Application')}: </b>
{rowData.application}
</span>
) : (
rowData.filename
),
},
{
title: this.i18n.translateToString('Column_Location'),
field: 'location',
sorting: true,
},
]}
...
everything works great. But I need to map the columns since the columns are dynamically generated depending on the received data.
The problem was, as is mostly the case, programmer-error. It took me quite a while to figure it out, but ended up coming right with the help from a colleague. With his guidance I finally managed to get a rough idea of where the problem originated.
In the end I found that the system threw an error since it could not find the column name. The (much higher up) error displayed in the console was very misleading as it pointed to the use of an invalid (null) reference, which turned out not to be the case. I had inadvertently, for some unknown reason, not used the generated unique column name in the last step where I added each row's data, but a hard-coded column name. Easy to overlook, but biting very hard ...

Set pagination on MaterialTable ui https://material-table.com/#/docs/features/localization?

That is my code. I would like to set pagination to 50, 100, 250 rows per page (for example). How can I achieve that?
<MaterialTable
localization={{
pagination: {
labelDisplayedRows: '4-6 of 10',
labelRowsPerPage:'{5, 10, 25,100}'
},
}}
title="Crawler Results"
columns={[
{ title: "URL", field: "url" },
{ title: "Status", field: "status" }
]}
data={state.data}
actions={[
{
icon: 'add',
tooltip: 'Add User',
isFreeAction: true,
onClick: () => {
//show crawler table and hide formular
document.getElementById('formCrawler').style.display = "block";
document.getElementById('formSpan').style.display = "block";
document.getElementById('crawlerTableID').style.display = "none";
}
}]}
/>
As mentioned in the docs, you can override the pageSizeOptions in options with an array of page sizes.
<MaterialTable
localization={{
pagination: {
labelDisplayedRows: '4-6 of 10',
labelRowsPerPage:'{5, 10, 25,100}'
},
}}
title="Crawler Results"
columns={[
{ title: "URL", field: "url" },
{ title: "Status", field: "status" }
]}
options={{
pageSizeOptions : [50, 100, 200]
}}
data={state.data}
actions={[
{
icon: 'add',
tooltip: 'Add User',
isFreeAction: true,
onClick: () => {
//show crawler table and hide formular
document.getElementById('formCrawler').style.display = "block";
document.getElementById('formSpan').style.display = "block";
document.getElementById('crawlerTableID').style.display = "none";
}
}]}
/>
Adding labelRowsPerPage didn't do anything for me. I added the following property to the MaterialTable:
options={{
pageSizeOptions: [50, 100, 250]
}}
https://material-table.com/#/docs/all-props

Resources