I'm creating a table where the data will be fetched from api and displayed in the mui table or material table. I wanna show different action button to different rows like in the first row the button should display "connect" when I click on that connect it should be changed into "disconnect" the problem here is it changes all the existing "connect" button into disconnect and again when the disconnect is clicked the button should change into connect. I want this behaviour to apply for that particular row alone not the entire table. How can I achieve this?
You should extent the array of data that you use on the Mui table by adding an extra fields for each row. One for saving the status ("connect" or "disconnect") and other for recognizing the row that must change.
After that you will be able to call the onClick event of the Button component and will affect only the target row.
Taking the example from Mui page on codesandbox, I added the "id" and "isConnected" prop to the data array. You can see the modified version in this link and also bellow:
function createData(id, name, calories, fat, carbs, protein, isConnected) {
return { id, name, calories, fat, carbs, protein, isConnected };
}
const data = [
createData(1, "Frozen yoghurt", 159, 6.0, 24, 4.0, true),
createData(2, "Ice cream sandwich", 237, 9.0, 37, 4.3, false),
createData(3, "Eclair", 262, 16.0, 24, 6.0, false),
createData(4, "Cupcake", 305, 3.7, 67, 4.3, false),
createData(5, "Gingerbread", 356, 16.0, 49, 3.9, false)
];
export default function BasicTable() {
const [rows, setRows] = React.useState(data);
const handleChangeConnect = (id) => {
console.log("The id is ", id);
setRows(
rows.map((row) => {
if (row.id == id) {
return { ...row, isConnected: !row.isConnected };
} else return { ...row };
})
);
};
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
<TableCell align="right">Is connected</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.name}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{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>
<TableCell align="right">
<Button
variant="contained"
color={row.isConnected ? "primary" : "secondary"}
onClick={() => {
handleChangeConnect(row.id);
}}
>
{row.isConnected ? "disconnect" : "connect"}
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
```l
Instead of using var for determining if it is connect or disconnect you need to use associative array for determine that example
if(check[index]==true){
<button onClick={handlechange}>Disconnect </button>
}
else {
<button onClick={handlechange}>connect </button>}
Related
how to remove the shadow from the table? tried to find the api but couldn't find the right property. took an example from the library they all come with a shadow. I don't understand how I can remove it.
shadow below
https://codesandbox.io/s/fchdj1?file=/demo.tsx
import * as React 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';
function createData(
name: string,
calories: number,
fat: number,
carbs: number,
protein: number,
) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
export default function DenseTable() {
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.name}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{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>
))}
</TableBody>
</Table>
</TableContainer>
);
}
The problem is not with the table component, it's the paper it has a default shadow .
You're using TableContainer with paper , just remove the component={Paper} it'll be set to div by default.
Table Screenshot
I have a table with items. I use cookies to save them. Also, I have two buttons (increase and decrease). When I press them to change qty it works, but it also puts the item's row to the bottom of the list. I need to keep them in the same position where they are. I used sortDirection prop to TableCell component to make an order, but it also didn't work. Please help update the code so I can keep items in the same position.
Thanks in advance.
export default function CartItemsTable() {
const [cookies, setCookie, removeCookie] = useCookies();
function IncreaseQTY(article) {
var newCookie = cookies[article];
newCookie.qty++;
setCookie(article, newCookie, {
expires: new Date(Date.now() + 604800000),
});
}
function DecreaseQTY(article) {
var newCookie = cookies[article];
newCookie.qty--;
if (newCookie.qty === 0) {
removeCookie(article);
} else {
setCookie(article, newCookie, {
expires: new Date(Date.now() + 604800000),
});
}
}
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} >
<TableHead>
<TableRow>
<TableCell >Name</TableCell>
<TableCell sortDirection="asc" align="center">Code</TableCell>
<TableCell align="center">Price</TableCell>
<TableCell align="center">QTY</TableCell>
<TableCell align="center">Total</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.keys(cookies).map(function (key, index) {
return (
<TableRow
key={index}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{cookies[key].name}
</TableCell>
<TableCell align="center">{cookies[key].article}</TableCell>
<TableCell align="center">{cookies[key].price}</TableCell>
<TableCell align="center">
<ButtonGroup
variant="contained"
aria-label="outlined primary button group"
>
<Button
onClick={() => {
DecreaseQTY(cookies[key].article);
}}
>
-
</Button>
<Button variant="text" disableRipple={true}>
{cookies[key].qty}
</Button>
<Button
onClick={() => {
IncreaseQTY(cookies[key].article);
}}
>
+
</Button>
</ButtonGroup>
</TableCell>
<TableCell align="center">
{cookies[key].qty * cookies[key].price}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
);
}
Before I press the button
After I press the increase button, first line went to the bottom
Update
I guess this problem can be occurred because of the cookies order or even related to react-cookie package, so I added console.log(cookies); to IncreaseQTY function. After a couple increasing it starts to show this way
The thing is i have dynamic tables which is coming from an API and on clicking the down arrow icon those specific data are getting fetched depending on the Table Names and are displayed as a table data but the problem is when user clicks on one row and after that if the user clicks on another row at the same time one Table data has to be shown or one row needs to get expanded at a time
like if i click the second row other rows will be closed only the second row table data will be visible
Row Component
import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '#material-ui/core/styles';
import Box from '#material-ui/core/Box';
import Collapse from '#material-ui/core/Collapse';
import IconButton from '#material-ui/core/IconButton';
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 TableRow from '#material-ui/core/TableRow';
import Typography from '#material-ui/core/Typography';
import Paper from '#material-ui/core/Paper';
import KeyboardArrowDownIcon from '#material-ui/icons/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#material-ui/icons/KeyboardArrowUp';
const useRowStyles = makeStyles({
root: {
'& > *': {
borderBottom: 'unset',
},
},
});
function createData(name, calories, fat, carbs, protein, price) {
return {
name,
calories,
fat,
carbs,
protein,
price,
history: [
{ date: '2020-01-05', customerId: '11091700', amount: 3 },
{ date: '2020-01-02', customerId: 'Anonymous', amount: 1 },
],
};
}
function Row(props) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const classes = useRowStyles();
return (
<React.Fragment>
<TableRow className={classes.root}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
<TableCell component="th" scope="row">
{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>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box margin={1}>
<Typography variant="h6" gutterBottom component="div">
History
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{row.history.map((historyRow) => (
<TableRow key={historyRow.date}>
<TableCell component="th" scope="row">
{historyRow.date}
</TableCell>
<TableCell>{historyRow.customerId}</TableCell>
<TableCell align="right">{historyRow.amount}</TableCell>
<TableCell align="right">
{Math.round(historyRow.amount * row.price * 100) / 100}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
Row.propTypes = {
row: PropTypes.shape({
calories: PropTypes.number.isRequired,
carbs: PropTypes.number.isRequired,
fat: PropTypes.number.isRequired,
history: PropTypes.arrayOf(
PropTypes.shape({
amount: PropTypes.number.isRequired,
customerId: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
}),
).isRequired,
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
protein: PropTypes.number.isRequired,
}).isRequired,
};
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0, 3.99),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3, 4.99),
createData('Eclair', 262, 16.0, 24, 6.0, 3.79),
createData('Cupcake', 305, 3.7, 67, 4.3, 2.5),
createData('Gingerbread', 356, 16.0, 49, 3.9, 1.5),
];
CollapsibleTable Component
export default function CollapsibleTable() {
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
{rows.map((row) => (
<Row key={row.name} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
dsdsd
you could add an open and onClick property to row that is populated by your table. example:
function Row(props: any) {
const { row } = props;
const classes = useRowStyles();
return (
<React.Fragment>
<TableRow className={classes.root}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => props.onClick(props.open ? "" : row.name)}>
{props.open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
<TableCell component="th" scope="row">
{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>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={props.open} timeout="auto" unmountOnExit>
<Box margin={1}>
<Typography variant="h6" gutterBottom component="div">
History
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{row.history.map((historyRow: any) => (
<TableRow key={historyRow.date}>
<TableCell component="th" scope="row">
{historyRow.date}
</TableCell>
<TableCell>{historyRow.customerId}</TableCell>
<TableCell align="right">{historyRow.amount}</TableCell>
<TableCell align="right">
{Math.round(historyRow.amount * row.price * 100) / 100}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
your table looks like this:
export default function CollapsibleTable() {
const [openRow, setOpenRow] = useState("")
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
{rows.map((row) => (
<Row key={row.name} row={row} open={row.name == openRow} onClick={(name) => setOpenRow(name)} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
How can I put two color backgrounds on two different lines?
I managed to set it up on a row before click on a table row and I would like to put a second backgroundcolor on another row when a condition is met
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import {TableBody, Table, TableCell, TableContainer, TableHead, TableRow, Paper} from "#material-ui/core";
import { useState } from "react";
const useStyles = makeStyles({
table: {
minWidth: 650
},
tableRow: {
"&$selected, &$selected:hover": {
backgroundColor: "#E8E8E8"
}
},
hover: {},
selected: {}
});
function createData(name, calories, fat, carbs, protein) { return { name, calories, fat, carbs, protein }; }
const rows = [ createData("Frozen yoghurt", 159, 6.0, 24, 4.0), createData("Ice cream sandwich", 237, 9.0, 37, 4.3), createData("Eclair", 262, 16.0, 24, 6.0), createData("Cupcake", 305, 3.7, 67, 4.3), createData("Gingerbread", 356, 16.0, 49, 3.9) ];
export default function SimpleTable() {
const classes = useStyles();
const [selectedIndex, setSelectedIndex] = useState(null);
const val = false;
const secondRow = 0;
const selectRow = (i) => {
setSelectedIndex(i);
if (val) {
// put a backgroundcolor on line 1
}
};
return (
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row, index) => (
<TableRow hover key={row.name} onClick={() => {selectRow(index);}} selected=selectedIndex === index} classes={{ hover: classes.hover, selected: classes.selected }} className={classes.tableRow} >
<TableCell component="th" scope="row">
{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>
))}
</TableBody>
</Table>
</TableContainer>
);
}
https://codesandbox.io/s/material-demo-forked-5u4jz?file=/demo.js
You can assign an array as your state for it to contain the selected indices. In the example below, I used shift on the array to retain the latest selection, remove the oldest one, and push the new one.
const [selectedIndex, setSelectedIndex] = useState([]);
const selectRow = (i) => {
// checks to see if it's already selected
if (selectedIndex.includes(i)) {
return;
}
let newSelected = [...selectedIndex];
// sample logic for the "only 2 rows selected" requirement
if (newSelected.length === 2) {
newSelected.shift();
newSelected.push(i);
} else {
newSelected.push(i);
}
setSelectedIndex(newSelected);
};
<TableRow
onClick={() => {
selectRow(index);
}}
selected={selectedIndex.includes(index)}
>
https://codesandbox.io/s/material-demo-forked-h5i77?file=/demo.js
I am trying to implement a simple onCellClick listener on Material UI table.
In earlier versions of it, a function in the table scope onCellClick used to give the row and column number where click was made as shown here
Currently when this function is placed -
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } 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 TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Paper from '#material-ui/core/Paper';
const styles = {
}
let id = 0;
function createData(name, calories, fat, carbs, protein) {
id += 1;
return { id, name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
function SimpleTable(props) {
const { classes } = props;
return (
<Paper className={classes.root}>
<Table className={classes.table}
onCellClick={(rowNumber,
columnId) =>
console.log(rowNumber,columnId)}>
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell numeric>Calories</TableCell>
<TableCell numeric>Fat (g)</TableCell>
<TableCell numeric>Carbs (g)</TableCell>
<TableCell numeric>Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => {
return (
<TableRow key={row.id}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell numeric>{row.calories}</TableCell>
<TableCell numeric>{row.fat}</TableCell>
<TableCell numeric>{row.carbs}</TableCell>
<TableCell numeric>{row.protein}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</Paper>
);
}
export default withStyles(styles)(SimpleTable);
it throws the error Unknown event handler property onCellClick. It will be ignored.This is expected because unlike previous versions no onCellClick function is passed in the table source code.
How to achieve onCellClick functionality now in Material-UI?
Any help will be highly appreciated.
A cell can be a th or td element (see the source code) or a custom element that you pass through the component property.
Each of these will have support for the onClick property.
So you need something like this:
handleClick = (id, column) => {
return (event) => {
console.log(`You clicked on row with id ${id}, in column ${column}.`);
}
}
render() {
return (
// ...
{this.state.rows.map((row) => (
<TableRow key={row.id}>
<TableCell onClick={this.handleClick(row.id, "calories")}>
{row.calories}
</TableCell>
// ...
<TableCell onClick={this.handleClick(row.id, "protein")}>
{row.protein}
</TableCell>
</TableRow>
)}
// ...
);
}