I am running into a bit of a problem grabbing data from a Data Grid (or XGrid) to display in another component on the page.
From the code below you can see that I am grabbing data from a local database that I am displaying in a Data Grid. What I want to do now is add the ability to take the selected rows and display the data (product ID, description, price, etc.) and display it in another component. Right now, I will settle for just a single selection.
I have tried many suggestions found here to add a onSelectionModelChange, but the only thing I am able to grab is the row id value. Not that it would make that much difference, but I am using MySQL for the backend.
Any suggestions?
Here's my React file:
import * as React from 'react';
import Axios from 'axios';
import { Grid } from '#material-ui/core';
import { createTheme } from '#material-ui/core/styles';
import { makeStyles } from '#material-ui/styles';
import { GridToolbarDensitySelector, GridToolbarFilterButton, XGrid } from '#material-ui/x-grid';
const currencyFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
});
const usdPrice = {
type: 'number',
width: 180,
valueFormatter: ({ value }) => currencyFormatter.format(Number(value)),
cellClassName: 'font-tabular-nums',
};
const defaultTheme = createTheme();
const useStyles = makeStyles(
(theme) => ({
root: {
padding: theme.spacing(0.5, 0.5, 0),
justifyContent: 'space-between',
display: 'flex',
alignItems: 'flex-start',
flexWrap: 'wrap',
background: '#f3f5f6'
},
textField: {
[theme.breakpoints.down('xs')]: {
width: '100%',
},
margin: theme.spacing(1, 0.5, 1.5),
'& .MuiSvgIcon-root': {
marginRight: theme.spacing(0.5),
},
'& .MuiInput-underline:before': {
borderBottom: `1px solid ${theme.palette.divider}`,
},
},
}),
{ defaultTheme },
);
function FilteringToolbar() {
const classes = useStyles();
return (
<div className={classes.root}>
<div>
<GridToolbarFilterButton />
<GridToolbarDensitySelector />
</div>
</div>
);
}
const ProductTable = () => {
const [rowData, setRowData] = React.useState([]);
const [rows, setRows] = React.useState(rowData);
React.useEffect(() => {
const axios = require('axios').default;
Axios.get("http://localhost:3001/api/get").then((response) => {
setRows(response.data);
}).catch(e => {
window.alert(e);
});
}, [rowData]);
const columns = [
{ field: 'prodID', headerName: 'Product ID', width: 130, disableReorder: true } ,
{ field: 'desc', headerName: 'Description', width: 200, editable: true, disableReorder: true },
{ field: 'price', headerName: 'Price', editable: true, disableReorder: true, ...usdPrice },
{ field: 'inStock', headerName: 'In Stock', width: 110, editable: true, disableReorder: true, headerAlign: 'center', align: 'center', type: 'boolean'},
{ field: 'new', headerName: 'New', width: 110, editable: true, disableReorder: true, headerAlign: 'center', align: 'center', type: 'boolean' },
]
return (
<Grid container item xs={12}>
<Grid container item xs={12}>
<Grid container item xs={3} />
<Grid container item xs={6} justifyContent="center" alignItems="center">
<div style={{ height: 500, width: '100%', display: 'block', marginLeft: 'auto', marginRight: 'auto' }}>
<XGrid checkboxSelection {...rows}
components={{ Toolbar: FilteringToolbar }}
rows={rows}
columns={columns}
id="id"
/>
</div>
</Grid>
<Grid container item xs={3} />
</Grid>
</Grid>
);
}
export default ProductTable;
I would suggest that instead of trying to get the row data that you need to pass to any child/related components from the table, you get the data directly from your own component's state. Since you already have the ids returned from onSelectionModelChange, you can simply use them to filter the rows state to get the selected items. For example:
...
const [selected, setSelected] = React.useState([]);
const [rows, setRows] = React.useState([]);
const handleSelectionChange = React.useCallback(
(selected = []) => {
setSelected(rows.filter(({ prodID }) => selected.includes(prodID)));
},
[rows]
);
...
<XGrid
// Calls our handler function that filters the selected items from state
onSelectionModelChange={handleSelectionChange}
checkboxSelection
{...rows}
components={{ Toolbar: FilteringToolbar }}
rows={rows}
columns={columns}
// Re-wires the grid to use your `prodID` instead of it's default `id`
getRowId={({ prodID }) => prodID}
/>
Working Code Sandbox: https://codesandbox.io/s/datagrid-selected-items-rm3f1?file=/src/App.tsx
Related
I have implemented the DataGrid in my component and have added a custom option Menu inside Action field. I am facing issue in passing id of the row on which the custom menu icon has been clicked. Can anyone guide me in getting rowId from the DataGrid Mui. I know there is a props called onCellClick but this only works if I click on the row.
Here is the code :
const dropdownItems = [
{
text: "View and Edit",
onClick: ()=> console.log("click and get id of the row"),
},
]
const renderThreeDotOption = () => {
return ( <MUITableCell
align="right"
padding="none"
width="40px"
>
<ActionDropdown
items={dropdownItems}
/>
</MUITableCell>)}
const columns = [
{
field: "property_name",
headerName: "Property",
flex:1,
},
{
field: "admin_name",
headerName: "Assignee",
flex: 1
},
{field: "actions", type: "actions", width: 200, renderCell:renderThreeDotOption}
]
return (
<div style={{ height: 'auto', overflow: "auto" , width: "100%" }}>
<DataGrid rows={data} columns={columns}
getRowId={(row) => row.property_id}
autoHeight={true}
rowsPerPageOptions={[5,10]}
pageSize={pageSize}
onPageSizeChange={(newPageSize) => setPageSize(newPageSize)}
pagination
onPageChange={handlePageChange}/>
</div>
)
so I have Texfield from Material UI, but i notice one things, every time i type, i stop and not continue to next letter, so i have to click again on it..., but when i remove the onChange method, the data continue to type without stopping..., is there because of the useEffect Component that prevent it from continously typing?
Here is my code:
import React from "react";
import { Search } from "#mui/icons-material";
import { IconButton, TextField, InputAdornment } from "#mui/material";
import {
GridToolbarDensitySelector,
GridToolbarContainer,
GridToolbarExport,
GridToolbarColumnsButton,
} from "#mui/x-data-grid";
import FlexBetween from "./FlexBetween";
const DataGridCustomToolbar = ({ searchInput, setSearchInput, setSearch }) => {
return (
<GridToolbarContainer>
<FlexBetween width="100%">
<FlexBetween>
<GridToolbarColumnsButton />
{/* <GridToolbarDensitySelector /> */}
<GridToolbarExport />
</FlexBetween>
<TextField
label="Search..."
sx={{ mb: "0.5rem", width: "15rem" }}
onChange={(e) => setSearchInput(e.target.value)}
value={searchInput}
variant="standard"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={() => {
setSearch({ value: searchInput });
setSearchInput("");
}}
>
<Search />
</IconButton>
</InputAdornment>
),
}}
/>
</FlexBetween>
</GridToolbarContainer>
);
};
export default DataGridCustomToolbar;
Here is what its being use on the datagrid
const BOM = () => {
const theme = useTheme();
// values to be sent to the backend
const [page, setPage] = useState(0);
const [limit, setlimit] = useState(20);
const [order, setOrder] = useState([
{
column: "1",
dir: "asc",
},
]);
const [search, setSearch] = useState({
value: "",
});
const [data, setData] = useState([]);
const [total, setTotal] = useState(0);
const setDataInput = (value) => {
setDataInput(value);
};
useEffect(() => {
dataService.getBomList(page, limit, order, search).then((response) => {
console.log("BOM");
setData(response.data.data.data);
setTotal(response.data.data.recordsFiltered);
return response.data.data.data;
});
}, [page, limit, order]);
console.log(data);
const [searchInput, setSearchInput] = useState("");
const columns = [
{
field: "sku",
headerName: "SKU",
width: 100,
},
{ field: "sku_name", headerName: "SKU NAME", width: 300 },
{
field: "plant",
headerName: "Plant",
width: 80,
align: "center",
headerAlign: "center",
},
{ field: "base_qty", headerName: "Base QTY", width: 100 },
{
field: "item",
headerName: "Item",
width: 60,
align: "center",
headerAlign: "center",
},
{ field: "uom_sku", headerName: "UOM SKU", width: 100, align: "center" },
{ field: "material", headerName: "Material", width: 150 },
{ field: "material_name", headerName: "Material Name", width: 300 },
{ field: "qty_material", headerName: "Material QTY", width: 100 },
{ field: "uom_material", headerName: "Material UOM", width: 100 },
{ field: "alt_bom", headerName: "BOM ALT", width: 80 },
{ field: "valid_from", headerName: "Valid From", width: 210 },
{ field: "valid_to", headerName: "Valid to", width: 210 },
{ field: "status", headerName: "Status", width: 80 },
];
return (
<Box m="5px">
<Box height="80vh">
{console.log("data", order, page, limit, search, searchInput)}
<StripedDataGrid
// loading={isLoading || !data}
getRowId={(row) => row.id}
columns={columns}
rowCount={(limit && total) || 0}
rowsPerPageOptions={[20, 50, 100]}
rows={data || []}
pagination
paginationMode="server"
sortingMode="server"
page={page}
pageSize={limit}
onPageSizeChange={(newPageSize) => setlimit(newPageSize)}
onPageChange={(newPage) => setPage(newPage)}
onSortModelChange={(newSortModel) => setOrder(...newSortModel)}
components={{ Toolbar: DataGridCustomToolbar }}
componentsProps={{
toolbar: { searchInput, setSearchInput, setSearch },
}}
/>
</Box>
</Box>
);
};
export default BOM;
Can someone tellme where did i do wrong here....
I am completely new to React and Material UI. I had followed the step from YouTube to create DataGrid, but now I want to create one more function which when I click on a row of DataGrid, the interface will jump to the detail page of the row.
Thank you so much!!
`
import { Box } from "#mui/material";
import { DataGrid, GridToolbar } from "#mui/x-data-grid";
import { tokens } from "../../theme";
import { mockDatareworks } from "../../data/mockData";
import Header from "../../components/Header";
import { useTheme } from "#mui/material";
const Reworks = () => {
const theme = useTheme();
const colors = tokens(theme.palette.mode);
const columns = [
{ field: "id", headerName: "ID", flex: 0.5 },
{ field: "return_date", headerName: "Date" },
{
field: "customer_name",
headerName: "Customer",
flex: 1,
cellClassName: "name-column--cell",
},
{
field: "product_name",
headerName: 'Product',
flex: 1,
},
{
field: "rework_quantity",
headerName: "Quantity",
type: "number",
headerAlign: "left",
align: "left",
},
];
return (
<Box m="20px">
<Header
title="Rework"
subtitle="List of reworks for Future Reference"
/>
<Box
m="40px 0 0 0"
height="75vh"
sx={{
"& .MuiDataGrid-root": {
border: "none",
},
"& .MuiDataGrid-cell": {
borderBottom: "none",
},
"& .name-column--cell": {
color: colors.greenAccent[300],
},
"& .MuiDataGrid-columnHeaders": {
backgroundColor: colors.blueAccent[700],
borderBottom: "none",
},
"& .MuiDataGrid-virtualScroller": {
backgroundColor: colors.primary[400],
},
"& .MuiDataGrid-footerContainer": {
borderTop: "none",
backgroundColor: colors.blueAccent[700],
},
"& .MuiCheckbox-root": {
color: `${colors.greenAccent[200]} !important`,
},
"& .MuiDataGrid-toolbarContainer .MuiButton-text": {
color: `${colors.grey[100]} !important`,
},
}}
>
<DataGrid
rows={mockDatareworks}
columns={columns}
components={{ Toolbar: GridToolbar }}
/>
</Box>
</Box>
);
};
export default Reworks;
`
This is my current code for the DataGrid page, how can I add the function of clicking and jump to the detail page of the row?
Please use onRowClick prop
https://mui.com/x/react-data-grid/events/
<DataGrid
rows={mockDatareworks}
columns={columns}
components={{ Toolbar: GridToolbar }}
onRowClick={handleRowClick} // here
/>
Here are handleRowClick paras
const handleRowClick = (
params, // GridRowParams
event, // MuiEvent<React.MouseEvent<HTMLElement>>
details, // GridCallbackDetails
) => {
setMessage(params);
};
This is what I'm trying to achieve, the current issues are:
the background is currently affecting only the container, I want it to take the entire place
there has to be space in between the cards and padding inside the cards
import { useState, useEffect } from 'react';
import type { NextPage } from 'next';
import Container from '#mui/material/Container';
import Box from '#mui/material/Box';
import { DataGrid, GridColDef } from '#mui/x-data-grid';
import { Card, Paper } from '#mui/material';
import Skeleton from '#mui/material/Skeleton';
import { amber, orange } from '#mui/material/colors';
import FormOne from './../src/FormOne';
const columns: GridColDef[] = [
{ field: 'id', headerName: 'ID' },
{ field: 'title', headerName: 'Title', width: 300 },
{ field: 'body', headerName: 'Body', width: 600 },
];
const LoadingSkeleton = () => (
<Box
sx={{
height: 'max-content',
}}
>
{[...Array(10)].map((_) => (
<Skeleton variant="rectangular" sx={{ my: 4, mx: 1 }} />
))}
</Box>
);
const Home: NextPage = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
// fetch data from fake API
useEffect(() => {
setInterval(
() =>
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((data) => {
setPosts(data);
setLoading(false);
}),
3000
);
}, []);
return (
<Container
maxWidth="lg"
sx={{
background: `linear-gradient(to right, ${amber[300]}, ${orange[500]})`,
}}
>
<Card>
<FormOne />
</Card>
<Card>
<Paper sx={{ height: '300px', width: '100%' }}>
<DataGrid
rows={posts}
columns={columns}
pageSize={10}
// autoHeight
rowsPerPageOptions={[10]}
disableSelectionOnClick
disableColumnMenu
disableColumnSelector
components={{
LoadingOverlay: LoadingSkeleton,
}}
loading={loading}
/>
</Paper>
</Card>
</Container>
);
};
export default Home;
First off, you will need to add remove margin and apply 100% of height to body and #root element. I have added this so style.css imported inside index.tsx
body {
margin: 0;
height: 100%;
}
#root {
height: 100%;
}
Next step would be to set maxWidth props to false, so it will be fulwidth.
I have added of course more stylings to your example to achieve the needed result(I hope i did it the way you imagined).
You can preview codesandbox here and edit the code here
p.s. I didnt have your FormOne component so I replaced it for now with simple input
Material-UI Select is lagging when there is more than 200 options.
It's a known issue - https://github.com/mui-org/material-ui/issues/17001
and I try to workaround it using useVirtual hook, of react-virtual.
so far it doesn't work well.
I created this sandbox: https://codesandbox.io/s/lucid-ritchie-j2c13?file=/src/Demo.tsx
if there is another way to virtualize the items in Material-UI Select, I'll take it.
this is my code:
import * as React from "react";
import Select from "#material-ui/core/Select";
import MUIMenuItem from "#material-ui/core/MenuItem";
import { useVirtual } from "react-virtual";
export default function Demo() {
const parentRef = React.useRef<any>();
const [value, setValue] = React.useState(1);
const rowVirtualizer = useVirtual({
size: 10000,
parentRef,
estimateSize: React.useCallback(() => 35, []),
overscan: 5
});
return (
<Select
MenuProps={{
ref: parentRef,
style: {
height: "200px",
width: `400px`,
overflow: "auto"
},
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
},
getContentAnchorEl: null,
MenuListProps: {
style: {
height: `${rowVirtualizer.totalSize}px`,
width: "100%",
position: "relative"
}
}
}}
value={value}
onChange={(e: any) => setValue(e.target.value)}
>
{rowVirtualizer.virtualItems.map(({ index, size, start }) => (
<MUIMenuItem
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: `${size}px`,
transform: `translateY(${start}px)`
}}
key={index}
value={String(index)}
>
<div>{index}</div>
</MUIMenuItem>
))}
</Select>
);
}