I am trying to make a search input that will filter by the name user will input. I have two components one is having the search input (app.js) and the other is having the table(table.js).
I am able to get the current value of the search input (app.js) but when trying to pass it as a props in the table (table.js) it's giving an error.
I am new to react but from my understanding, I think I'm having issues with passing the useState as a props.
1. Code for search input (app.js)
I used onChange event as to get the current value when the input field change.
<div className="search">
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={getSearchTerm}
/>
</div>
</div>
getSearchTerm function to get current input value
const [searchTerm, setSearchTerm] = useState("");
const getSearchTerm = (event) => {
const searchWord = event.target.value;
console.log(searchWord);
setSearchTerm(searchWord)
}
2. Table (app.js) passing the props
passing the props in the filter function. So can get filterd when a input would be entered in search input.
export default function EnhancedTable(props) {
console.log("these r props for table component", props);
<TableBody>
{data
.filter((item) => {
if (props.searchTerm == "") {
return item;
} else if (item.clientName.toLowerCase().includes(props.searchTerm.toLowerCase())) {
return item;
}
})
.map((item, index) => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
>
<TableCell padding="checkbox">
<Checkbox
/>
</TableCell>
<TableCell component="th" scope="row" padding="none">{item.clientName}</TableCell>
<TableCell align="right">{item.clientEmail}</TableCell>
<TableCell align="right">{item.clientWorkPhone}</TableCell>
<TableCell align="right">{item.clientIndustry}</TableCell>
<TableCell align="right">{item.tenantId}</TableCell>
<TableCell align="right">{item.clientWebsite}</TableCell>
<TableCell align="right"><Button style={{ backgroundColor: 'transparent', color: '#5900B4' }} variant="outlined" color="primary" href="#outlined-buttons" >{<CreateIcon />}</Button>
</TableCell>
</TableRow>
)
})}
</TableBody>
3. Error getting
[The error I'm getting is here][1]
4. Full Search input (app.js) file for more clarity
function App() {
const [searchTerm, setSearchTerm] = useState("");
const classes = useStyles();
const getSearchTerm = (event) => {
//console.log(inputEl.current.value);
const searchWord = event.target.value;
console.log(searchWord);
setSearchTerm(searchWord)
}
return (
<div className="App">
<div className="wrapper">
<div className="container-table">
<div className="head">
<h5 className='management'>MANAGEMENT</h5>
<div className="head-middle">
<h2>Clients</h2>
<div className="button-collection">
<Button style={{ backgroundColor: '#5900B4', color: '#FFFFFF', fontSize: '15px', fontWeight: '900', width: '206px', height: '42px' }}
variant="contained"
className='add-collection-btn'
startIcon={<AddIcon />}
>
New Collection
</Button>
</div>
</div>
<div className="head-bottom">
<div className="head-button">
<div className="search">
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={getSearchTerm}
/>
</div>
</div>
<Button style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px', marginLeft: '20px', marginRight: '20px' }} variant="contained">Search</Button>
<Button style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px' }} variant="contained">Clear</Button>
</div>
<Button style={{
backgroundColor: 'transparent', color: '#5900B4', width: '206px', height: '42px', borderColor: '#5900B4', fontSize: '15px', fontWeight: '900'
}} variant="outlined" color="primary"
startIcon={<FilterListIcon />}
>
SHOW FILTER
</Button>
</div>
<div className="table">
<EnhancedTable
onChange={setSearchTerm}
/>
</div>
</div>
</div>
</div>
</div>
);
}
export default App;
5. Full table (table.js) file for clarity
const headCells = [
{ id: 'name', numeric: false, disablePadding: true, label: 'Client Name' },
{ id: 'email', numeric: true, disablePadding: false, label: 'Email' },
{ id: 'phone', numeric: true, disablePadding: false, label: 'Phone' },
{ id: 'industry', numeric: true, disablePadding: false, label: 'Industry' },
{ id: 'contact', numeric: true, disablePadding: false, label: 'Point of Contact' },
{ id: 'website', numeric: true, disablePadding: false, label: 'Website' },
{ id: 'btn-icon', numeric: true, disablePadding: false, label: '' },
];
function EnhancedTableHead(props) {
const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount } = props;
return (
<TableHead>
<TableRow style={{ backgroundColor: '#F5F6F8', height: '120px' }}>
<TableCell padding="checkbox">
<Checkbox
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={onSelectAllClick}
inputProps={{ 'aria-label': 'select all desserts' }}
/>
</TableCell>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.numeric ? 'right' : 'left'}
padding={headCell.disablePadding ? 'none' : 'normal'}
>
{headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
);
}
EnhancedTableHead.propTypes = {
classes: PropTypes.object.isRequired,
numSelected: PropTypes.number.isRequired,
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
orderBy: PropTypes.string.isRequired,
};
const useToolbarStyles = makeStyles((theme) => ({
root: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
highlight:
theme.palette.type === 'light'
? {
color: theme.palette.secondary.main,
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
}
: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.secondary.dark,
},
title: {
flex: '1 1 100%',
},
}));
const EnhancedTableToolbar = (props) => {
const classes = useToolbarStyles();
return (
<Toolbar>
{
<Typography className={classes.title} variant="h6" id="tableTitle" component="div">
Clients
</Typography>
}
</Toolbar>
);
};
EnhancedTableToolbar.propTypes = {
numSelected: PropTypes.number.isRequired,
};
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
paper: {
width: '100%',
marginBottom: theme.spacing(2),
},
table: {
minWidth: 750,
},
visuallyHidden: {
border: 0,
clip: 'rect(0 0 0 0)',
height: 1,
margin: -1,
overflow: 'hidden',
padding: 0,
position: 'absolute',
top: 20,
width: 1,
},
}));
export default function EnhancedTable(props) {
console.log("these r props for table component", props);
const classes = useStyles();
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('calories');
const [selected, setSelected] = React.useState([]);
const [page, setPage] = React.useState(0);
const [dense, setDense] = React.useState(false);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const isSelected = (name) => selected.indexOf(name) !== -1;
const [data, setData] = useState([]);
const getData = async () => {
try {
const data = await axios.get("something");
setData(data.data);
} catch (e) {
console.log("this is error for fetching data", e)
}
};
useEffect(() => {
getData();
}, [])
return (
<div className={classes.root}>
<Paper className={classes.paper}>
<EnhancedTableToolbar numSelected={selected.length} />
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
size={dense ? 'small' : 'medium'}
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
numSelected={selected.length}
order={order}
orderBy={orderBy}
/>
<TableBody>
{data
/*.filter((item) => {
if (searchTerm == "") {
return item;
} else if (item.clientName.toLowerCase().includes(searchTerm.toLowerCase())) {
return item;
}
})*/
.map((item, index) => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
>
<TableCell padding="checkbox">
<Checkbox
/>
</TableCell>
<TableCell component="th" scope="row" padding="none">{item.clientName}</TableCell>
<TableCell align="right">{item.clientEmail}</TableCell>
<TableCell align="right">{item.clientWorkPhone}</TableCell>
<TableCell align="right">{item.clientIndustry}</TableCell>
<TableCell align="right">{item.tenantId}</TableCell>
<TableCell align="right">{item.clientWebsite}</TableCell>
<TableCell align="right"><Button style={{ backgroundColor: 'transparent', color: '#5900B4' }} variant="outlined" color="primary" href="#outlined-buttons" >{<CreateIcon />}</Button>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
</TableContainer>
</Paper>
</div>
);
}
The issue is that searchTerm is not within scope of your component so this line...
item.clientName.toLowerCase().includes(searchTerm.toLowerCase())
throws the error you're seeing
Cannot read property 'toLowerCase' of undefined
You need to pass in the required props to your component.
<EnhancedTable searchTerm={searchTerm} />
This is also a perfect opportunity to use React's useMemo hook to produce the filtered results
const { searchTerm } = props // extract searchTerm from props
const filtered = useMemo(() => {
if (!searchTerm) {
return data
}
const term = searchTerm.toLowerCase()
return data.filter(({ clientName }) =>
clientName.toLowerCase().includes(term))
}, [ data, searchTerm ])
Now you can just use filtered in place of data in your return value
<TableBody>
{filtered.map((item, index) => {
// etc
})}
</TableBody>
Related
The code below I have provided , one example opens the dialog. And the dialog has add functionality which is the addEmail where in you can add multiple fields which is inside the dialog.
I wanna know is that when I close the dialog using the onClick={handleClose} it should reset the dialog, fields that I added should not show after I close since I did not save it.
So when I click cancel it should reset the state.
Thanks for any idea.
for example here I added fields when I close this and open again these field should not show cause it should reset when I close.
#interface.ts
export type EditPropertiesProps = {
open: boolean;
handleClose: () => void;
selectedRow: IRegional
};
#Code snippet - main page --- this calls and opens the dialog
const handleClose = () => {
console.log('here')
setOpen(false);
};
<EditProperties open={open} handleClose={handleClose} selectedRow={selectedRow} />
#EditProperties ts code
export const RegionalListData: IRegionalList[] = [
{
id: 4,
name: "Associate Director of Construction Ops",
column: "associateDirectorofConstructionOps",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
{
id: 5,
name: "CAM Manager",
column: "camManager",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
{
id: 6,
name: "CAO-Chief Administrative Officer",
column: "caoChiefAdministrativeOfficer",
emails: [
{
emailAddress: "associateDir#gmail.com",
firstName: "Associate",
lastName: "Director",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
},
];
type InitialReqPaylod = {
accountId: number;
regionalRoleUserDto: IRegional;
};
type IData = {
regionName: string;
marketName: string;
subRegionName: string;
};
type IEmail = {
emailAddress: string;
firstName: string;
id: number;
lastName: string;
};
const EditProperties: FC<EditPropertiesProps> = ({
open,
handleClose,
selectedRow,
}) => {
const dispatch = useAppDispatch();
const [isEmailOpen, setOpenEmail] = useState(false);
const [fetching, setFetching] = useState(false);
const [RegionalList, setRegionalList] = useState<IRegionalList[]>(
RegionalListData
);
const [data, setData] = useState<IData>({
regionName: "",
marketName: "",
subRegionName: "",
});
const [regionalId, setRegionalId] = useState<number | null>(null);
const [emailCurrentIndex, setEmailCurrentIndex] = useState<number | null>(
null
);
const [selectedEmailId, setSelectedEmailId] = useState<number | null>(null);
const { isSuccess } = useAppSelector((state) => state.yardUser);
const { isSaveSuccess } = useAppSelector((state) => state.region);
const email = useAppSelector((state) => state.yardUser);
const [emailOptions, setEmailOptions] = useState<IEmail[]>([]);
const emailList = email.data ? email.data.data : [];
useEffect(() => {
if (selectedRow) {
setData({
regionName: selectedRow["regionName"],
marketName: selectedRow["marketName"],
subRegionName: selectedRow["subRegionName"],
});
let regional = [...RegionalList];
for (const k in selectedRow) {
regional.map((prop: IRegionalList) => {
if (prop.column === k) {
prop.emails = selectedRow[k] ? selectedRow[k] : [];
}
});
}
setRegionalList(regional);
}
}, [selectedRow]);
const [maxWidth, setMaxWidth] = React.useState<DialogProps["maxWidth"]>("md");
const fetchEmailResult = React.useMemo(
() =>
throttle(
(event: any, callback: (results: IEmail[]) => void) => {
const payload: IYardUserRequestPayload | InitialReqPaylod = {
accountId: 1,
searchString: event.target.value,
};
fetch(
`https://jsonplaceholder.typicode.com/users?email=${event.target.value}`
)
.then((res) => res.json())
.then((res) => res.data ? callback(res.data.slice(0, 10)) : callback([]))
},
200
),
[]
);
const emailOnChange = (event: any, regionalId: number, index: number, emailId: number) => {
setRegionalId(regionalId);
setEmailCurrentIndex(index);
setSelectedEmailId(emailId);
fetchEmailResult(event,(results: IEmail[]) => {
console.log('results' , results)
if (results.length) setEmailOptions(results);
});
};
useEffect(() => {
if (isSaveSuccess) {
handleClose();
}
}, [isSaveSuccess]);
useEffect(() => {
if (isSuccess) {
setFetching(false);
}
}, [isSuccess]);
const addEmail = (id: number) => {
setRegionalList((list) =>
list.map((item) => {
if (item.id === id) {
return {
...item,
emails: [
...item.emails,
{
emailAddress: "",
firstName: "",
lastName: "",
id: Math.floor(Math.random() * 999),
fetching: false,
},
],
};
}
return item;
})
);
};
const deleteEmail = (email: IEmail, regionId: number) => {
const regionalListCopy = [...RegionalList].map((prop: IRegionalList) => {
if (prop.id === regionId) {
return {
...prop,
emails: prop.emails.filter((prop) => prop.id !== email.id),
};
}
return { ...prop };
});
setRegionalList(regionalListCopy);
};
const setOnChangeOption = (email) => {
setSelectedEmailId(null);
setRegionalList((list) =>
list.map((item) => {
if (item.id === regionalId) {
return {
...item,
emails: [
...item.emails.map((prop) => {
return {
...prop,
...email,
};
}),
],
};
}
return item;
})
);
};
const EmailItem = ({ email, mIndex, prop }) => (
<>
<div style={{ display: "block" }} key={email.id}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: 15
}}
>
<Autocomplete
options={emailOptions}
getOptionLabel={(option: IEmail) => option.emailAddress}
onInputChange={($event) => emailOnChange($event, prop.id, mIndex, email.id)}
onChange={($event, value) => setOnChangeOption(value)}
fullWidth
open={email.id === selectedEmailId}
renderInput={(params) => (
<TextField size="small" {...params} variant="standard" />
)}
renderOption={(props, option) => {
return (
<Box component="li" {...props}>
{option.emailAddress}
</Box>
);
}}
/>
<DeleteIcon
style={{ color: "red", cursor: "pointer" }}
onClick={() => deleteEmail(email, prop.id)}
/>
</div>
<div
style={{
fontSize: ".8em",
display: "flex",
justifyContent: "space-between",
}}
>
<span style={{ paddingTop: 5 }}>
Email : {email.emailAddress}
Full Name: {email.firstName} {email.lastName}
</span>
{/* <span style={{ paddingRight : 40 }}>{fetching ? "Fetching...." : null}</span> */}
</div>
</div>
</>
);
return (
<Dialog
maxWidth={maxWidth}
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Edit</DialogTitle>
<DialogContent>
<Card sx={{ minWidth: 275 }} style={{ padding: 20 }}>
<div>
<span>Sub-Region (Sub-Division)</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Region (Division)"
variant="filled"
value={data.regionName}
/>
</FormControl>
</div>
<div style={{ marginTop: 10 }}>
<span>Sub-Region (Sub-Division)</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Sub-Region (Sub-Division)"
variant="filled"
value={data.subRegionName}
/>
</FormControl>
</div>
<div style={{ marginTop: 10 }}>
<span>Market</span>
<Divider style={{ marginTop: 10 }} />
<FormControl sx={{ mt: 2, minWidth: 720 }}>
<TextField
label="Market"
variant="filled"
value={data.marketName}
/>
</FormControl>
</div>
</Card>
{RegionalList.map((prop: IRegionalList, index: number) => (
<Card
sx={{ minWidth: 275 }}
style={{ overflow: "visible", padding: 20, marginTop: 20 }}
key={prop.id}
>
<div style={{ display: "flex", alignItems: "center" }}>
{prop.name}*{" "}
<AddIcon
style={{ marginLeft: 5, cursor: "pointer" }}
onClick={() => addEmail(prop.id)}
/>
</div>
<Divider style={{ marginTop: 10 }} />
{prop.emails.map((email: IEmail, mIndex: number) => (
<EmailItem
key={email.id}
prop={prop}
email={email}
mIndex={mIndex}
/>
))}
</Card>
))}
</DialogContent>
<DialogActions
style={{ marginTop: "20px", marginRight: "20px", marginBottom: "20px" }}
>
<Button onClick={handleClose}>Cancel</Button>
<Button variant="contained" onClick={() => saveChanges()} autoFocus>
Save Changes
</Button>
</DialogActions>
</Dialog>
);
};
export default EditProperties;
You need just reset all used states as values of form when clicking handleClose, my suggestion would be to use just one object state for form values.
Example:
const onClose = () => {
handleClose();
setRegionalList(RegionalListData);
}
return (
<Dialog
maxWidth={maxWidth}
open={open}
onClose={onClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
why is my code showing wrong output for
as the rows has been already populated after adding it with the handletabledata function
My code is having a table and there is a add button where we add users but on the delete function when I am trying to see the rows I am getting only that row and rows above it.
please help.
import React from 'react'
import './server.scss'
import Paper from '#mui/material/Paper';
import Table from '#mui/material/Table';
import TableBody from '#mui/material/TableBody';
import TableCell from '#mui/material/TableCell';
import TableContainer from '#mui/material/TableContainer';
import TableHead from '#mui/material/TableHead';
import TablePagination from '#mui/material/TablePagination';
import TableRow from '#mui/material/TableRow';
import { Button } from '#mui/material';
import DeleteOutlineIcon from '#mui/icons-material/DeleteOutline';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import DialogTitle from '#mui/material/DialogTitle';
import Slide from '#mui/material/Slide';
import PersonAddAltIcon from '#mui/icons-material/PersonAddAlt';
import TextField from '#mui/material/TextField';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import Select from '#mui/material/Select';
const columns = [
{ id: 'key', label: '#', minWidth: 70 },
{ id: 'user', label: 'User', minWidth: 100 },
{
id: 'signed',
label: 'Last Signed in',
minWidth: 170,
align: 'right',
},
{
id: 'role',
label: 'Role',
minWidth: 170,
align: 'right',
},
{
id: 'icon',
label: '',
minWidth: 170,
align: 'right',
},
];
function createData(user, role, key, icon, signed) {
signed = signed.getTime()
return { key, user, signed, role, icon };
}
//modal code
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
const Server = () => {
//final insertion of data
const [rows, setrow] = React.useState([]);
// console.log(rows)
//pagination
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
//modal code
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
//setting data for row
const [id, setid] = React.useState(0);
const handletabledata = () => {
setid(id + 1)
let row = [...rows]
row.push(createData(email, role, id, <DeleteOutlineIcon onClick={console.log(rows)}/>, new Date()))
setrow(row)
setemail('')
setrole('')
setOpen(false);
}
//input field
const [email, setemail] = React.useState('');
const handleEmail = (event) => {
setemail(event.target.value);
};
//dropdown list
const [role, setrole] = React.useState('');
const handleChange = (event) => {
setrole(event.target.value);
};
return (
<div className='server_main'>
<div className='addUser'>
<Button variant="contained" onClick={handleClickOpen}>Add User</Button>
<Dialog
open={open}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-describedby="alert-dialog-slide-description"
fullWidth={true}
>
<DialogTitle>{"Add User"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-slide-description">
<div style={{ display: 'flex', height: '250px', justifyContent: 'space-around' }}>
<div >
<PersonAddAltIcon style={{ fontSize: '7rem', position: 'relative', top: '50px' }} />
</div>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
<span style={{ padding: '10px', position: 'relative', left: '10px', top: '10px' }}>User Information</span>
<TextField placeholder="Email Address" onChange={handleEmail} value={email} />
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Role</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={role}
label="role"
onChange={handleChange}
>
<MenuItem value={'Owner'}>Owner</MenuItem>
<MenuItem value={'Sales'}>Sales</MenuItem>
<MenuItem value={'Admin'}>Admin</MenuItem>
</Select>
</FormControl>
</div>
</div>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handletabledata}>Add</Button>
</DialogActions>
</Dialog>
</div>
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
<TableContainer sx={{ maxHeight: 440 }}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{ minWidth: column.minWidth }}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={row.code}>
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id} align={column.align}>
{column.format && typeof value === 'number'
? column.format(value)
: value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 100]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</Paper>
</div>
);
}
export default Server
I'm working with MUI table components. I have made the below table example with pagination.
const MuiTable = () => {
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const [data, setData] = useState([]);
useEffect(() => {
setData(tableData);
}, []);
const emptyRows =
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - data.length) : 0;
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
return (
<TableContainer sx={{ maxHeight: "300px" }} component={Paper}>
<Table stickyHeader aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Id</TableCell>
<TableCell>First Name</TableCell>
<TableCell>Last Name</TableCell>
<TableCell align="center">Email</TableCell>
</TableRow>
</TableHead>
<TableBody>
{(rowsPerPage > 0
? data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: data
).map((row) => (
<TableRow
key={row.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell>{row.id}</TableCell>
<TableCell>{row.first_name}</TableCell>
<TableCell>{row.last_name}</TableCell>
<TableCell align="center">{row.email}</TableCell>
</TableRow>
))}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
count={data.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
SelectProps={{
inputProps: {
"aria-label": "rows per page",
},
}}
labelDisplayedRows={({ page }) => {
return `Page: ${page}`;
}}
backIconButtonProps={{
color: "secondary",
}}
nextIconButtonProps={{ color: "secondary" }}
showFirstButton={true}
showLastButton={true}
labelRowsPerPage={<span>Rows:</span>}
sx={{
".MuiTablePagination-toolbar": {
backgroundColor: "rgba(100,100,100,0.5)",
},
".MuiTablePagination-selectLabel, .MuiTablePagination-input": {
fontWeight: "bold",
color: "blue",
},
}}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
);
};
I would like to know how I can make a sticky column (I would like to have the same effect as stickyHeader). On the other hand, I have tried to center out the pagination component by adding this on the TablePagination component but it is not centering the component:
sx={{
'.MuiTablePagination-root': {
display: 'flex',
justifyContent: 'center',
},
}}
I would appreciate any help on the regard.
sx={{ display: 'flex', justifyContent: 'center' }} works for me
I have made a table with a search bar functionality, it filter the data when press the search button and reset the filter function and show unfilter data when click the clear button but it's not clearing the current input value from the display. however it clear the filetr function and show unfilter data. I tired setting state to empty string but still not able to clear the input value, I'm new in react need assistance to understand the issue
1. App.js having search bar and all the state and function
function App() {
const [searchTerm, setSearchTerm] = useState("");
const classes = useStyles();
const [buttonSearch, setButtonSearch] = useState("");
const getSearchTerm = (event) => {
let searchWord = event.target.value;
setSearchTerm(searchWord);
console.log(searchWord);
}
const doSearch = () => {
console.log('this is from the doSearch func', searchTerm)
setButtonSearch(searchTerm);
}
const clearSearch = () => {
console.log('im working')
setSearchTerm("");
setButtonSearch("");
}
return (
<div className="App">
<div className="wrapper">
<div className="container-table">
<div className="head">
<h5 className='management'>MANAGEMENT</h5>
<div className="head-middle">
<h2>Clients</h2>
<div className="button-collection">
<Button style={{ backgroundColor: '#5900B4', color: '#FFFFFF', fontSize: '15px', fontWeight: '900', width: '206px', height: '42px' }}
variant="contained"
className='add-collection-btn'
startIcon={<AddIcon />}
>
New Collection
</Button>
</div>
</div>
<div className="head-bottom">
<div className="head-button">
<div className="search">
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={getSearchTerm}
/>
</div>
</div>
<Button onClick={doSearch}
style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px', marginLeft: '20px', marginRight: '20px' }} variant="contained">
Search
</Button>
<Button onClick={clearSearch}
style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px' }} variant="contained">
Clear
</Button>
</div>
<Button
style={{ backgroundColor: 'transparent', color: '#5900B4', width: '206px', height: '42px', borderColor: '#5900B4', fontSize: '15px', fontWeight: '900' }}
variant="outlined" color="primary"
startIcon={<FilterListIcon />}
>
SHOW FILTER
</Button>
</div>
<div className="table">
<EnhancedTable
searchTerm={buttonSearch}
/>
</div>
</div>
</div>
</div>
</div>
);
}
2. table.js having filter and map function
export default function EnhancedTable(props) {
console.log("these r props for table component", props);
const { searchTerm } = props;
console.log("table searchTerm value", searchTerm)
const classes = useStyles();
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('calories');
const [selected, setSelected] = React.useState([]);
const [page, setPage] = React.useState(0);
const [dense, setDense] = React.useState(false);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const isSelected = (name) => selected.indexOf(name) !== -1;
const [data, setData] = useState([]);
const getData = async () => {
try {
const data = await axios.get("something");
console.log('This is data from axios', data.data);
setData(data.data);
} catch (e) {
console.log("this is error for fetching data", e)
}
};
useEffect(() => {
getData();
}, [])
const filtered = useMemo(() => {
if (!searchTerm) {
return data;
}
const term = searchTerm.toLowerCase()
return data.filter(({ clientName, clientEmail }) => clientName.toLowerCase().includes(term)
|| clientEmail.toLowerCase().includes(term)
)
}, [data, searchTerm])
return (
<div className={classes.root}>
<Paper className={classes.paper}>
<EnhancedTableToolbar numSelected={selected.length} />
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
size={dense ? 'small' : 'medium'}
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
numSelected={selected.length}
order={order}
orderBy={orderBy}
/>
<TableBody>
{filtered
.map((item, index) => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
>
<TableCell padding="checkbox">
<Checkbox
/>
</TableCell>
<TableCell component="th" scope="row" padding="none">{item.clientName}</TableCell>
<TableCell align="right">{item.clientEmail}</TableCell>
<TableCell align="right">{item.clientWorkPhone}</TableCell>
<TableCell align="right">{item.clientIndustry}</TableCell>
<TableCell align="right">{item.tenantId}</TableCell>
<TableCell align="right">{item.clientWebsite}</TableCell>
<TableCell align="right">
<Button style={{ backgroundColor: 'transparent', color: '#5900B4' }}
variant="outlined" color="primary" href="#outlined-buttons" >
{<CreateIcon />}
</Button>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
</TableContainer>
</Paper>
</div>
);
}
Try this option
<InputBase value={searchTerm}
My design currently stands like this
I am using Material UI's radio button, i want to make each row select able just once. When i add a <RadioButton> component, i am able to select it but however i am not able to toggle between rows
transactionRow(member: Object) {
return (
<tr id='drawLotteryTabel' style={styles.tr} key={member.uuid}>
<td className="col-md-2 col-xs-2">{member.user.fullName}</td>
<td className="col-md-1 col-xs-1">{this.getSubscriptionDropDown(member.subscriptions)}</td>
<td className="col-md-2 col-xs-2">{CommonConstants.INR_SYMBOL + ' ' + Utils.formatNumberLocalised(member.bidDiscountAmount || 0)}</td>
<td className="col-md-2 col-xs-2">{CommonConstants.INR_SYMBOL + ' ' + Utils.formatNumberLocalised(member.bidDiscountPercent || 0)}</td>
<td className="col-md-2 col-xs-2">{CommonConstants.INR_SYMBOL + ' ' + Utils.formatNumberLocalised(member.unpaidAmount || 0)}</td>
<td
className="col-md-2 col-xs-2"
>
<RadioButtonGroup name="shipSpeed" defaultSelected="not_light" key={member.uuid}>
<RadioButton
value="light"
style={styles.radioButton}
/>
</RadioButtonGroup></td>
</tr>
);
}
The Above code results in something like this
What should i do so that i get the entire table row as a single entity to select.
You'll probably, unfortunately, need to manage the state of your radio buttons manually, as a controlled component.
Have the RadioButtonGroup include a call to an onChange function that stores the selected value:
handleChange: (event, value) => {
this.setState({selectedButtonValue: value});
}
And then push that value to every RadioButtonGroup using the valueSelected property as such;
<RadioButtonGroup name="shipSpeed" defaultSelected="not_light" key={member.uuid}
onChange={this.handleChange}
valueSelected={this.state.selectedButtonValue}
>
I assume you are using "Enhanced Table" from material ui.
If that's the case
replace
if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1));
} else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selected.slice(0, selectedIndex),
selected.slice(selectedIndex + 1),
);
}
with
if (selectedIndex === -1) {
newSelected = [name];
}
and replace
The Checkbox component with Radio component.
change this
<TableCell padding="checkbox">
<Checkbox
checked={isItemSelected}
inputProps={{ 'aria-labelledby': labelId }}
/>
</TableCell>
to this
<TableCell padding="checkbox">
<Radio
checked={isItemSelected}
inputProps={{ 'aria-labelledby': labelId }}
/>
</TableCell>
You should be able to toggle between the radio buttons as the button only activates when new row is selected and it replaces the old selected state.
I am attaching the implementation of material ui enhanced table with radio button implementation. I did some tweak here and there. You can take a look.
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { lighten, makeStyles } from '#material-ui/core/styles';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TablePagination from '#material-ui/core/TablePagination';
import TableRow from '#material-ui/core/TableRow';
import TableSortLabel from '#material-ui/core/TableSortLabel';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Paper from '#material-ui/core/Paper';
import Radio from '#material-ui/core/Radio';
// import IconButton from '#material-ui/core/IconButton';
// import Tooltip from '#material-ui/core/Tooltip';
// import FormControlLabel from '#material-ui/core/FormControlLabel';
// import Switch from '#material-ui/core/Switch';
// import DeleteIcon from '#material-ui/icons/Delete';
// import FilterListIcon from '#material-ui/icons/FilterList';
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Donut', 452, 25.0, 51, 4.9),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Gingerbread', 356, 16.0, 49, 3.9),
createData('Honeycomb', 408, 3.2, 87, 6.5),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Jelly Bean', 375, 0.0, 94, 0.0),
createData('KitKat', 518, 26.0, 65, 7.0),
createData('Lollipop', 392, 0.2, 98, 0.0),
createData('Marshmallow', 318, 0, 81, 2.0),
createData('Nougat', 360, 19.0, 9, 37.0),
createData('Oreo', 437, 18.0, 63, 4.0),
];
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) return order;
return a[1] - b[1];
});
return stabilizedThis.map((el) => el[0]);
}
const headCells = [
{ id: 'name', numeric: false, disablePadding: true, label: 'Dessert (100g serving)' },
{ id: 'calories', numeric: true, disablePadding: false, label: 'Calories' },
{ id: 'fat', numeric: true, disablePadding: false, label: 'Fat (g)' },
{ id: 'carbs', numeric: true, disablePadding: false, label: 'Carbs (g)' },
{ id: 'protein', numeric: true, disablePadding: false, label: 'Protein (g)' },
];
function EnhancedTableHead(props) {
const { classes, order, orderBy, onRequestSort } = props;
const createSortHandler = (property) => (event) => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
<TableCell padding="checkbox">
</TableCell>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.numeric ? 'right' : 'left'}
padding={headCell.disablePadding ? 'none' : 'default'}
sortDirection={orderBy === headCell.id ? order : false}
>
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : 'asc'}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<span className={classes.visuallyHidden}>
{order === 'desc' ? 'sorted descending' : 'sorted ascending'}
</span>
) : null}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
);
}
EnhancedTableHead.propTypes = {
classes: PropTypes.object.isRequired,
onRequestSort: PropTypes.func.isRequired,
onSelectAllClick: PropTypes.func.isRequired,
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
orderBy: PropTypes.string.isRequired,
rowCount: PropTypes.number.isRequired,
};
const useToolbarStyles = makeStyles((theme) => ({
root: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
highlight:
theme.palette.type === 'light'
? {
color: theme.palette.secondary.main,
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
}
: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.secondary.dark,
},
title: {
flex: '1 1 100%',
},
}));
const EnhancedTableToolbar = (props) => {
const classes = useToolbarStyles();
const { numSelected } = props;
return (
<Toolbar
className={clsx(classes.root, {
[classes.highlight]: numSelected.length > 0,
})}
>
{numSelected.length > 0 ? (
<Typography className={classes.title} color="inherit" variant="subtitle1" component="div">
{numSelected}
</Typography>
) : (
<Typography className={classes.title} variant="h6" id="tableTitle" component="div">
Nutrition
</Typography>
)}
{/* {numSelected.length > 0 ? (
<Tooltip title="Delete">
<IconButton aria-label="delete">
<DeleteIcon />
</IconButton>
</Tooltip>
) : (
<Tooltip title="Filter list">
<IconButton aria-label="filter list">
<FilterListIcon />
</IconButton>
</Tooltip>
)} */}
</Toolbar>
);
};
EnhancedTableToolbar.propTypes = {
numSelected: PropTypes.string.isRequired,
};
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
paper: {
width: '100%',
marginBottom: theme.spacing(2),
},
table: {
minWidth: 750,
},
visuallyHidden: {
border: 0,
clip: 'rect(0 0 0 0)',
height: 1,
margin: -1,
overflow: 'hidden',
padding: 0,
position: 'absolute',
top: 20,
width: 1,
},
}));
export default function EnhancedTableWithRadio() {
const classes = useStyles();
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('calories');
const [selected, setSelected] = React.useState('');
const [page, setPage] = React.useState(0);
// const [dense, setDense] = React.useState(false);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const handleRequestSort = (event, property) => {
const isAsc = orderBy === property && order === 'asc';
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(property);
};
const handleClick = (event, name) => {
let newSelected = selected;
if (name !== selected) {
newSelected = name;
}
setSelected(newSelected);
};
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
// const handleChangeDense = (event) => {
// setDense(event.target.checked);
// };
const isSelected = (name) => selected.indexOf(name) !== -1;
const emptyRows = rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);
return (
<div className={classes.root}>
<Paper className={classes.paper}>
<EnhancedTableToolbar numSelected={selected} />
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
// size={dense ? 'small' : 'medium'}
size = "medium"
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
rowCount={rows.length}
/>
<TableBody>
{stableSort(rows, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
const isItemSelected = isSelected(row.name);
const labelId = `enhanced-table-checkbox-${index}`;
return (
<TableRow
hover
onClick={(event) => handleClick(event, row.name)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.name}
selected={isItemSelected}
>
<TableCell padding="checkbox">
<Radio
checked={isItemSelected}
inputProps={{ 'aria-labelledby': labelId }}
/>
</TableCell>
<TableCell component="th" id={labelId} scope="row" padding="none">
{row.name}
</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
{/* <TableRow style={{ height: (dense ? 33 : 53) * emptyRows }}> */}
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper>
{/* <FormControlLabel
control={<Switch checked={dense} onChange={handleChangeDense} />}
label="Dense padding"
/> */}
</div>
);
}
I had the same problem using a Radio button component inside a cell from a Table, both components from MaterialUi. I resolve like this:
This is my handleChange function to select de value of the Row.id that i use for my app
handleChange(field, event) {
this.setState({[field] : event.target.value});
}
And in my Table component i just use the "Radio" component without a "RadioButtonGoup" component. And use a condition in the "checked" prop of the "Radio" component to show it as checked or not.
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell align="center">Selecciona</TableCell>
<TableCell>Brand</TableCell>
<TableCell align="center">Tarjetahabiente</TableCell>
<TableCell align="center">Terminación</TableCell>
<TableCell align="center">Expira</TableCell>
<TableCell align="center">Eliminar</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (
<TableRow key={row.id}>
<TableCell component="th" scope="row">
<Radio
value={row.id}
defaultSelected={false}
checked={row.id != this.state.paymentSourceId ? false : true}
onChange={this.handleChange.bind(this, 'paymentSourceId')}
/>
</TableCell>
<TableCell align="center">{row.brand}</TableCell>
<TableCell align="center">{row.name}</TableCell>
<TableCell align="center">{row.last4}</TableCell>
<TableCell align="center">{row.exp_month}/{row.exp_year}</TableCell>
<TableCell align="center">
<IconButton
aria-label="delete"
color="primary"
onClick={()=>this.handleDelete(this.state.paymentSourceId)}>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>