Sort columns in Table in ascending and descending way - javascript

I have created a Table with React and MUI, and tried to implement a sorting function for each column in ascending and descending way.
But when I click on the header to sort the column, I get an error, that says "Data is not iterable" in the console, and I have not been able to figure out where the mistake is.
import React, {useEffect, useState} from 'react';
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 TableRow from '#mui/material/TableRow';
import Paper from '#mui/material/Paper';
import TableSortLabel from "#mui/material/TableSortLabel";
import TablePagination from '#mui/material/TablePagination';
import { createStyles, TextField } from '#mui/material';
import "./table.css";
function createUserData (fullname, email, nationality, city){
return {fullname, email, nationality, city};
}
const rows= [];
export default function RandomUserTable(){
const [data, setData]= useState([]);
// const [rowData, setRowData]= useState(rows);
// const [orderDirection, setOrderDirection]= useState("asc");
const [order, setOrder]=useState('asc');
const [searchTerm, setSearchTerm]= useState('');
const [page, setPage] =useState(0);
const [rowsPerPage, setRowsPerPage] =useState(10);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
useEffect(()=>{
fetch('https://randomuser.me/api/?results=30')
.then((response) => {
return response.json();
})
.then((data) => {
setData(data);
console.log(data);
});
}, []);
const sorting=(col)=>{
if(order==='asc'){
const sorted= [...data].sort((a,b)=>
a[col].toLowerCase()>b[col].toLowerCase()?1:-1
);
setData(sorted);
setOrder('dsc')
}
if(order==='dsc'){
const sorted= [...data].sort((a,b)=>
a[col].toLowerCase()<b[col].toLowerCase()?1:-1
);
setData(sorted);
setOrder('asc')
}
}
// const sortArray= (arr, orderBy)=> {
// switch (orderBy){
// case "asc":
// return [...arr].sort ((a,b)=>
// a.city> b.city? 1:b.city > a.city? -1:0
// );
// case "desc":
// return [...arr].sort((a, b)=>
// a.city < b.city ? 1 : b.city< a.city ? -1 : 0
// );
// }
// };
// const handleSortRequest = ()=>{
// setRowData(sortArray(rows, orderDirection));
// setOrderDirection(orderDirection === "asc"? "desc":"asc");
// }
return (
<>
<TextField sx={{ width: 660, marginBottom: "2rem", marginTop: "2rem", border: "2px solid violet" }} className="text-field" onChange={event=>{setSearchTerm(event.target.value)}} placeholder="Search..."></TextField>
<TableContainer component={Paper} sx={{width:800, margin: "0 auto"}}>
<Table stickyHeader className="table" aria-label="simple table">
<TableHead>
<TableRow sx={{'& .MuiTableCell-root':{backgroundColor:"rgb(120, 169, 233)", border: "1px solid white", color: 'white'}}}>
<TableCell align="center">Fullname</TableCell>
<TableCell onClick={()=>sorting("email")} align="center">E-Mail</TableCell>
<TableCell onClick={()=>sorting("nat")} align="center">Nationality</TableCell>
{/* <TableCell align="center" onClick={handleSortRequest}> */}
<TableCell align="center">
<TableSortLabel style={{color: 'white'}} active={true}>City</TableSortLabel>
{/* <TableSortLabel style={{color: 'white'}} active={true} direction={orderDirection}>City</TableSortLabel> */}
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.results?.slice(page*rowsPerPage, page*rowsPerPage+rowsPerPage).filter((val)=>{
if(searchTerm=== ""){
return val
}else if(val.nat.toLowerCase().includes(searchTerm.toLowerCase()) ){
return val
}else if(val.email.toLowerCase().includes(searchTerm.toLowerCase())){
return val
}else if(val.location.city.toLowerCase().includes(searchTerm.toLowerCase())){
return val
}
}).map((user,index) => (
<TableRow key={index} sx={{'& .MuiTableCell-root':{backgroundColor: 'rgba(54, 96, 129, 0.241)', height: '3rem'}}}>
<TableCell align="center" component="th" scope="row">
{ (user.name.first)+ " " +(user.name.last)}
</TableCell>
<TableCell align="center">{user.email}</TableCell>
<TableCell align="center">{user.nat}</TableCell>
<TableCell align="center">{user.location.city}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 100]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</>
);
}
enter image description here

you cant spread object in the array like this
let data= {info:0,result:[1,2,3]}
let spread = [...data]
that's why you get an error "Data is not iterable"
do this instead
const sorting=(col)=>{
if(order==='asc'){
const sorted= {...data,["results"]:data.results.sort((a,b)=>
a[col].toLowerCase()>b[col].toLowerCase()?1:-1
)}
setData(sorted);
setOrder('dsc')
}
if(order==='dsc'){
const sorted= {...data,["result"]: data.results.sort((a,b)=>
a[col].toLowerCase()<b[col].toLowerCase()?1:-1
)}
setData(sorted);
setOrder('asc')
}
}

Related

Want to add a Collapse Button on the Table to expand it on the front end

import React, { useState, useEffect, Fragment } from "react";
import classes from "../CSS/DataUpload.module.css";
import * as XLSX from "xlsx";
import axios from "axios";
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 TableRow from "#mui/material/TableRow";
import Paper from "#mui/material/Paper";
import { Button } from "#mui/material";
let initialSend = true;
const DataUpload = () => {
const [sendData, setSendData] = useState([]);
const [addData, setAddData] = useState(false);
const [display, setDisplay] = useState(false);
const [notification, setNotification] = useState();
useEffect(() => {
// console.log(sendData);
// let payload = JSON.parse(sendData);
// console.log("type of ", typeof payload);
const excelData = async () => {
// console.log(se)
try {
console.log(sendData);
const url = "http://localhost:5000/users";
const config = {
"Content-Type": "application/json",
};
const response = await axios.post(url, sendData, config);
const msg = response.data.msg;
setNotification(msg);
} catch (error) {
console.log(error);
}
};
if (initialSend) {
initialSend = false;
return;
} else {
setDisplay(true);
excelData();
}
}, [addData]);
const onChange = (e) => {
const [file] = e.target.files;
const reader = new FileReader();
reader.onload = (evt) => {
const bstr = evt.target.result;
const wb = XLSX.read(bstr, { type: "binary" });
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
const data = XLSX.utils.sheet_to_json(ws, {
header: [
"no",
"name",
"vuln_type",
"summary",
"tech_details",
"remediation",
"req",
"res",
"cvss_score",
"cvss_eq",
"severity",
],
blankrows: false,
});
data.shift();
setSendData(data);
};
reader.readAsBinaryString(file);
};
const addDataHandler = () => {
setAddData(true);
};
const removeNotification = () => {
setDisplay(false);
};
// };
return (
<Fragment>
{display && (
<div
style={{
backgroundColor: "red",
marginLeft: "100px",
marginRight: "100px",
}}
>
<p style={{ color: "white" }}>{notification}</p>
<button
style={{ float: "right", color: "white" }}
onClick={removeNotification}
>
X
</button>
</div>
)}
<div className={classes.excelupload}>
<p>Choose Excel File to Upload</p>
<input type="file" id="input" onChange={onChange}></input>
</div>
<div style={{ margin: "50px" }}>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow sx={{ fontSize: 16 }}>
<TableCell>No</TableCell>
<TableCell align="right">Name</TableCell>
<TableCell align="right">Vuln Type</TableCell>
<TableCell align="right">Summary</TableCell>
<TableCell align="right">Tech Details</TableCell>
<TableCell align="right">Remediation</TableCell>
<TableCell align="right">req</TableCell>
<TableCell align="right">res</TableCell>
<TableCell align="right">cvss score</TableCell>
<TableCell align="right">cvsseq</TableCell>
<TableCell align="right">severity</TableCell>
</TableRow>
</TableHead>
<TableBody>
{sendData.map((row) => (
<TableRow
key={row.no}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.no}
</TableCell>
<TableCell align="right">{row.name}</TableCell>
<TableCell align="right">{row.vuln_type}</TableCell>
<TableCell align="right">{row.summary}</TableCell>
<TableCell align="right">{row.tech_details}</TableCell>
<TableCell align="right">{row.remediation}</TableCell>
<TableCell align="right">{row.req}</TableCell>
<TableCell align="right">{row.res}</TableCell>
<TableCell align="right">{row.cvss_score}</TableCell>
<TableCell align="right">{row.cvss_eq}</TableCell>
<TableCell align="right">{row.severity}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</div>
</Fragment>
);
};
export default DataUpload;[enter image description here][1]
This the code for my front end. I want to add a collapse button to the request and response field. I have no idea about how to do it as I am a newbie in this field. I am making this project for our company. It fetches the data from MongoDB which is the back end. It would be really great if anyone can suggest how to do it. Thanks in advance.
You are looking for the Collapse component.
Just put your table into Collapse component, sth like ↓
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(!open)}>
{open ? "Collapse" : "Expand"}
</button>
<Collapse in={open}>
<TableContainer component={Paper}>
....
</TableContainer>
</Collapse>
</>
)
Here is the demo I've created, you can take a look.
Update
I think you need sth like this example, horizontal collapse, am I right?
The example was not created by me though, I think it can give you some hints.

Can't get all users from MySQL database users table using Express in reactJS

I am using react-material-dashboard to view stats for the Admin.
I want to show all my users in a table to view in the admin dashboard. I am using Express to get the users from my DB but when I run in the browser I get a GET http://localhost:3001/api/fetchUsers 404 (Not Found) error.
How can I display all my users in my react-material-dashboard?
What am I doing wrong?
here is my structure:
CustomersListResults.js:
import { useState, useEffect } from 'react';
import PerfectScrollbar from 'react-perfect-scrollbar';
import {
Box,
Card,
Table,
TableCell,
TableHead,
TablePagination,
TableRow,
} from '#material-ui/core';
import Axios from 'axios';
const CustomerListResults = () => {
const [limit, setLimit] = useState(10);
const [page, setPage] = useState(0);
useEffect(() => {
Axios.get('http://localhost:3001/api/fetchUsers').then((response) => {
console.log(response);
});
}, []);
const handleLimitChange = (event) => {
setLimit(event.target.value);
};
const handlePageChange = (event, newPage) => {
setPage(newPage);
};
return (
<Card>
<PerfectScrollbar>
<Box sx={{ minWidth: 1050 }}>
<Table>
<TableHead>
<TableRow>
<TableCell>
Account
</TableCell>
<TableCell>
Name
</TableCell>
<TableCell>
Email
</TableCell>
<TableCell>
Location
</TableCell>
<TableCell>
Phone
</TableCell>
<TableCell>
Registration date
</TableCell>
<TableCell>
Action
</TableCell>
</TableRow>
</TableHead>
</Table>
</Box>
</PerfectScrollbar>
<TablePagination
component="div"
count={5}
onPageChange={handlePageChange}
onRowsPerPageChange={handleLimitChange}
page={page}
rowsPerPage={limit}
rowsPerPageOptions={[5, 10, 25]}
/>
</Card>
);
};
export default CustomerListResults;
CustomersList:
import { Helmet } from 'react-helmet';
import { Box, Container } from '#material-ui/core';
import CustomerListResults from '../components/customer/CustomerListResults';
import CustomerListToolbar from '../components/customer/CustomerListToolbar';
const CustomerList = () => (
<>
<Helmet>
<title>Customers | </title>
</Helmet>
<Box
sx={{
backgroundColor: 'background.default',
minHeight: '100%',
py: 3
}}
>
<Container maxWidth={false}>
<CustomerListToolbar />
<Box sx={{ pt: 3 }}>
<CustomerListResults />
</Box>
</Container>
</Box>
</>
);
export default CustomerList;
My Server API:
//API To Get All Users
app.get("api/fetchUsers", (req, res) => {
db.query("SELECT * FROM users", (req, res) => {
if (err){
console.log(err)
}else{
console.log(result);
res.send(result)
}
});
});
Change in below line, add / in start:
app.get("/api/fetchUsers", (req, res) => {})

When changing the state of parent from child component setState function becomes undefined

Okay so I have a really strange error in my react application.
Basically I have a function that handles an input change and changes the state from the parent component.
import React from "react";
import PaginationExtended from "../PaginationExtended/PaginationExtended";
import { ScreenContext } from "../../helpers/context";
import {
Paper,
TableCell,
TableContainer,
Table,
TableBody,
TableHead,
TableRow,
Checkbox,
TextField,
} from "#material-ui/core";
import EnhancedTableToolbar from "../EnhancedToolBar/EnhancedToolBar";
import uuid from "react-uuid";
import findWithAttr from "../../helpers/findAttr";
import objectInArray from "../../helpers/objectInArray";
import ItemTableRow from "./ItemTableRow/ItemTableRow";
const ItemTable = (props) => {
const screen = React.useContext(ScreenContext);
const handleCheckboxClick = (e) => {
if (e.target.checked) {
props.setDynamicRows((prevState) => {
const newTableData = [...prevState];
console.log(newTableData);
const rowIndex = findWithAttr(props.rows, "id", parseInt(e.target.id));
const newRow = {};
props.headings.forEach((heading) => {
newRow[heading.text.toLowerCase()] =
props.rows[rowIndex][heading.default];
});
newRow.id = e.target.id;
newRow.type = "accepted";
newRow.files = [];
newRow.category = props.title;
newTableData.push(newRow);
return newTableData;
});
} else {
props.setDynamicRows((prevState) => {
const newTableData = [...prevState];
console.log(newTableData);
const rowIndex = findWithAttr(
newTableData,
"id",
parseInt(e.target.id)
);
newTableData.splice(rowIndex, 1);
return newTableData;
});
}
};
const handleInputChange = () => {
console.log(1);
props.setDynamicRows((prevState) => {
const newTableData = [...prevState];
console.log(newTableData);
const rowIndex = findWithAttr(newTableData, "id", props.row.id);
newTableData[rowIndex][e.target.name] = e.target.value;
return newTableData;
});
};
return (
<Paper className={`${props.className}`}>
<EnhancedTableToolbar
screen={screen}
title={props.title}
// record={props.rows}
// setRows={setRows}
/>
<TableContainer>
<Table aria-label="simple table">
<TableHead>
<TableRow>
{props.hasCheckbox && <TableCell padding="checkbox" />}
{props.headings.map((heading) => {
return (
<TableCell className={"text-nowrap"} align="right">
{heading.text}
</TableCell>
);
})}
</TableRow>
</TableHead>
<TableBody>
{props.rows.map((row, index) => {
if (!props.inputTable) {
return (
<ItemTableRow
title={props.title}
headings={props.headings}
handleCheckboxClick={handleCheckboxClick}
dynamicRows={props.dynamicRows}
index={index}
row={row}
hasCheckbox={props.hasCheckbox}
setDynamicRows={props.setDynamicRows}
/>
);
} else {
if (row.type === "accepted" && row.category === props.title) {
return (
<ItemTableRow
title={props.title}
headings={props.headings}
handleCheckboxClick={handleCheckboxClick}
dynamicRows={props.dynamicRows}
index={index}
row={row}
hasCheckbox={props.hasCheckbox}
handleInputChange={handleInputChange}
/>
);
}
}
})}
</TableBody>
</Table>
</TableContainer>
{props.hasPagination && (
<PaginationExtended
recordsPerPage={rowsPerPage}
className={"p-3 justify-content-end"}
totalRecords={rows.length}
paginate={handleChangePage}
currentPage={page}
shape={"round"}
/>
)}
</Paper>
);
};
export default ItemTable;
This function is called from a child component of the component at the top.
import React from "react";
import { TableCell, TableRow, Checkbox, TextField } from "#material-ui/core";
import objectInArray from "../../../helpers/objectInArray";
const ItemTableRow = (props) => {
return (
<TableRow key={props.index}>
{props.hasCheckbox && (
<TableCell padding="checkbox">
<Checkbox
id={props.row.id}
checked={objectInArray(
props.dynamicRows,
"id",
props.row.id.toString()
)}
onClick={props.handleCheckboxClick}
// inputProps={{
// "aria-labelledby": `${props.title}-${index}`,
// }}
/>
</TableCell>
)}
{props.headings.map((heading) => {
return (
<TableCell
className={"text-nowrap"}
id={`${props.title}-${props.index}`}
align="right"
>
{heading.isTextInput ? (
<TextField
onChange={props.handleInputChange}
id={`${props.title}-${heading.text}-${props.row.id}`}
type={heading.inputType}
size={"small"}
variant="outlined"
inputProps={{ min: 0 }}
notched={false}
InputLabelProps={{ shrink: false }}
className={`minInputWidth`}
InputProps={{
classes: { input: "py-2 customize-inputField" },
}}
name={heading.text.toLowerCase()}
placeholder={heading.text}
/>
) : (
<p>{props.row[heading.default]}</p>
)}
</TableCell>
);
})}
</TableRow>
);
};
export default ItemTableRow;
When I trigger this event the following error appears in the console.
Uncaught TypeError: props.setDynamicRows is not a function
The curious thing about this is that the handleCheckboxClick function does not throw any error and is also called called from the child component. I have tried reestructuring the components and functions but nothing works.
If someone knows what is happening I would really appreciate the help, thanks.

How to subtract only selected value from Array

Hello here is my problem, I have implemented a table that table fill when I choose some suggestion. The table contains three columns of (product, qty, balance and add button) So when I click add button it displays some popup form.
So I want to do when adding some quantity in the popup menu and after that click the add button it will reduce from the balance field in the table according to the selected product. Below is the table array.
import React, { useState } from "react"
import {
Grid,
Table,
TableContainer,
TableHead,
TableRow,
TableCell,
Paper,
TableBody,
Button
} from "#material-ui/core"
import AddItem from "./AddItem"
import { useStyles } from "../../../../styles/classes"
export default function ItemsList({
cart,
handleChange,
handleBlur,
values,
setFieldValue,
volumes
}) {
const [open, setOpen] = useState(false)
const [productId, setProductId] = useState("")
const [factoryId, setFactoryId] = useState("")
const handleClickOpen = (productId, factoryId) => {
setProductId(productId)
setFactoryId(factoryId)
setOpen(true)
}
const handleClose = (value) => {
setOpen(false)
}
const addToList = (factory) => {
setOpen(false)
const product = cart.find(
(item) => item.productId === Number(productId)
)
const item = {
productId: productId,
productName: product.productName,
containerNo: values.containerNo,
sealNo: values.sealNo,
qty: values.qty,
factoryId: factory.id,
factory: factory.name,
printDate: values.printDate,
printTime: values.printTime,
palletNo: values.palletNo,
}
const idx = values.volumes.findIndex(e=>e.number === Number(values.noOfContainers))
console.log(idx)
const current = values.volumes[idx].data;
current.push(item)
setFieldValue(`volumes[${idx}].data`,current)
//sum of qty in loaded volumes for this product
values.invoiceItems.forEach((item, idx) => {
item.balance = (Number(item.balance) - Number(values.qty))
})
setFieldValue('invoiceItems', values.invoiceItems)
}
const classes = useStyles()
return (
<Grid item md={12} sm={12} xs={12}>
<TableContainer component={Paper} >
<Table className={classes.table} size="small">
<TableHead>
<TableRow>
<TableCell align="left">Product</TableCell>
<TableCell align="left">Quantity</TableCell>
<TableCell align="left">Balance</TableCell>
<TableCell align="left">Add</TableCell>
</TableRow>
</TableHead>
<TableBody>
{cart.map((row, i) => (
<TableRow key={i}>
<TableCell align="left">{row.productName}</TableCell>
<TableCell align="left">{row.qty}</TableCell>
<TableCell align="left">{row.balance}</TableCell>
<TableCell align="left">
<Button
variant="outlined"
size="small"
color="secondary"
onClick={() => {
handleClickOpen(row.productId)
}}>
add
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<AddItem
open={open}
onClose={handleClose}
handleChange={handleChange}
handleBlur={handleBlur}
values={values}
addToList={addToList}
volumes={volumes}
/>
</Grid>
)
}
import React, { useState} from "react"
import {
Grid,
Card,
CardContent,
CardActions,
Button,
CardHeader,
} from "#material-ui/core"
import Alert from "#material-ui/lab/Alert"
import axios from 'axios'
//validation
import { Formik, Form } from "formik"
import * as Yup from 'yup'
import { useStyles } from "../../../styles/classes"
import ItemList from "./subComponents/ItemsList"
import MetaData from "./subComponents/MetaData"
import ContainerList from "./subComponents/ContainerList"
import {useInitialValues} from '../../../hooks/useLoadingInfoData'
// validation schema
let validationSchema = Yup.object().shape({
volumes: Yup.string().required(),
})
export default function LoadingInfo(props) {
const classes = useStyles()
const [initialValues, setInitialValues] = useInitialValues()
const [volumes, setVolumes] = useState([])
const [alert, setAlert] = useState({
showAlert: false,
severity: "success",
message: "",
})
// create method
const submit = async (e, { resetForm }) => {
try {
await axios.post("/loadingInfo", e)
resetForm()
setAlert({
showAlert: true,
severity: "success",
message: "Loading Information created successfully!",
})
} catch (error){
if (error.response.status === 422) {
setAlert({
showAlert: true,
severity: 'error',
message: 'Loading information already exists!',
})
} else {
setAlert({
showAlert: true,
severity: 'error',
message: 'Loading information creation failed!',
})
}
}
}
return (
<Grid container className={classes.root} spacing={1}>
<Grid item xs={12} sm={12} md={12}>
<Formik
initialValues={initialValues}
onSubmit={submit}
validationSchema={validationSchema}
enableReinitialize>
{({
isValid,
dirty,
values,
handleChange,
handleBlur,
setFieldValue,
errors,
}) => {
return (
<Form>
<Card variant="outlined" style={{marginBottom: '1rem'}}>
<CardHeader title="Loading Information"></CardHeader>
<CardContent>
<Grid container spacing={1}>
<MetaData
values={values}
handleChange={handleChange}
handleBlur={handleBlur}
setAlert={setAlert}
setFieldValue={setFieldValue}
/>
<ItemList
cart={values.invoiceItems}
values={values}
handleChange={handleChange}
handleBlur={handleBlur}
setFieldValue={setFieldValue}
volumes={volumes}
/>
</Grid>
</CardContent>
</Card>
<Card variant="outlined">
<CardContent>
{values.volumes.map( (data , idx) =>
<ContainerList values={data} idx={idx} setFieldValue={setFieldValue}/>
)}
</CardContent>
<CardActions>
<Button
variant="contained"
color="primary"
type="submit"
disabled={!dirty || !isValid}>
create
</Button>
</CardActions>
</Card>
</Form>
)
}}
</Formik>
</Grid>
<Grid></Grid>
{alert.showAlert && (
<Grid item md={12}>
<Alert
severity={alert.severity}
onClose={() =>
setAlert({
...alert,
showAlert: false,
})
}>
{alert.message}
</Alert>
</Grid>
)}
</Grid>
)
}

Adding a Radio button of material UI inside row cell in table,

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>

Categories