New Dataset not Reflecting Table Display - reactjs

I am able to print the updated value of dataset but the new added row did not reflecting the updated data from the table component display.
I temporarily put the event on keypress to trigger for adding new row on the table, here the simple code that I playing.
function createData(hardware, cost, status) {
return { hardware, cost, status };
}
const tblData = [];
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
tblData: [],
};
}
render() {
return (
<><><Box
sx={{
width: 500,
maxWidth: '100%',
}}
>
<TextField
fullWidth
label="Scan Here"
id="fullWidth"
onKeyPress={(e) => {
if (e.key === 'Enter') {
//*****--------------------------------------------------
// - Here is the part where I was able to get the updated data but not reflecting on the table display
console.log('Enter key pressed');
tblData.push(createData('blah', 356, 'Yes'));
console.log(tblData);
//
//--------------------------------------------------*****
}
} } />
</Box><TableContainer component={Paper}>
<Table sx={{ minWidth: 650, marginTop: '10px' }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Hardware</TableCell>
<TableCell align="right">Cost</TableCell>
<TableCell align="right">status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{tblData.map((row) => (
<TableRow
key={row.name}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.hardware}
</TableCell>
<TableCell align="right">{row.cost}</TableCell>
<TableCell align="right">{row.status}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer></>
)
}
}
export default Main;
Any suggestion or comments how to refresh the updated data. TIA

Just pushing element to the state doesn't trigger rerendering.
<TextField
fullWidth
label="Scan Here"
id="fullWidth"
onKeyPress={(e) => {
console.log('Enter key pressed');
this.setState((prev) => {
tblData: prev.tblData.concat([createData('blah', 356, 'Yes')]) // this line is edited.
}))
}}
/>
Hope this would be helpful for you.

It's working now, I just missed the setState to refresh the value.
console.log('Enter key pressed');
tblData.push(createData('blah', 356, 'Yes'));
this.setState({
setState: tblData
});

Related

sortDirection prop of TableCell doesn't work

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

Single selection grid

Hello I am trying to add selection logic to my material ui grid(table).
This is what I return,
return (
<Paper sx={{ width: "100%", overflow: "hidden" }}>
<Table style={{ width: "100%" }}>
<CatalogRightGridHeader selected={selected} />
<TableBody xs={12} style={{ width: "100%" }}>
{value.map((row) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={row.nodeId}>
<TableCell style={{ width: "10px!important", padding: 2 }}>
<Checkbox/>
</TableCell>
{row.rowItems
.sort(
(row1, row2) => row1.column.sequence - row2.column.sequence
)
.map((column) => {
return column.column.dataType === "DATE" ? (
<TableCell
key={column.id}
align={column.align}
>
{format(
new Date(parseInt(column.value)),
dateFormat(),
new Date()
)}
</TableCell>
) : (
<TableCell
key={column.id}
align={column.align}
>
{column.value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</Paper>
);
I want to have a logic on checkbox so whenever I check them I want to get checked row data and it should uncheck whenever I check another checkbox. I would appreciate if u could give me some tips how to do it or help me doing it.
You should take a look at MUI's DataGrid which comes with the checkbox selections.
Also, it sounds like you want these to act like radio buttons, which, the UX difference is that checkboxes are like multiple selects, whereas radio buttons infer that only one can be selected.
If you want to stay with a vanilla table, then adding in the checkbox like that is fine, from there, when you map, add in an index. The checkboxes need to be controlled
const [checked, setChecked] = React.useState(null)
const handleCheckChange = (event, index) => {
// If checkbox is changed to checked, set the index
if (event.target.checked) {
setChecked(index)
}
// Since on change is fired every time the state changes, it'll fire when it
// gets unchecked by checking something else, so only set back to null when
// it's unchecked while being the actively checked row
else if (!event.target.checked && index === checked) {
setChecked(null)
}
}
value.map((row, index) => {
return (
<TableRow hover role='checkbox' tabIndex={-1} key={row.nodeId}>
<TableCell style={{ width; '10px!important', padding: 2 }}>
<Checkbox
checked={checked === index}
onChange={(event) => handleCheckChange(event, index)}
/>
</TableCell>
...
)
}
From here, you now know the row that is checked and can pull that data as
rowData[checked]

React - How to break a line in a cell of a material ui table

I'm using the DenseTable component of the ui material and wanted to break a string into parts
here is my table:
I want in the cursos column, in the line that has LEIT - 1st Ano/LEE - 1st Ano I want to break the string where there is the '/'
below my DenseTable component code:
function createData(aulaID, numAula, tipoAula, nome, curso, data, acao) {
return { aulaID, numAula, tipoAula, nome, curso, data, acao };
}
export default function DenseTable() {
const { selectedRow, setSelectedRow, cursoSigla } = useListarAulas();
const [rows, setRows] = React.useState([]);
const {
showLancarSumarioModal,
handleOpenLancarSumarioModal,
handleCloseLancarSumarioModal,
} = useEscreverSumario();
const {
aulas,
// docente,
} = useFiltrarAulas();
React.useEffect(() => {
if (rows.length !== aulas.length) {
const tempRows = [];
aulas.map((aula) =>
tempRows.push(
createData(
aula.id,
aula.numero,
aula.tipo,
aula.disciplina.codigo,
cursoSigla(aula.cursos),
aula.data,
'Lançar Sumario'
)
)
);
setRows(tempRows);
}
}, [aulas, rows]);
return (
<>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Disciplina</TableCell>
<TableCell align="right">Curso</TableCell>
<TableCell align="right">Data</TableCell>
<TableCell align="right">Acção</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.nome}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.nome}
</TableCell>
<TableCell align="right">{row.curso}</TableCell>
<TableCell align="right">
{row.data}
{/* moment(row.data).format('yyyy-MM-DD') */}
</TableCell>
<TableCell align="right">
<Button
size="small"
onClick={() => {
setSelectedRow(row);
handleOpenLancarSumarioModal();
}}
>
{row.acao}
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<EscreverSumarioModal
showModal={showLancarSumarioModal}
handleCloseModal={handleCloseLancarSumarioModal}
selectedRow={selectedRow}
/>
</>
);
}
the function cursoSigla() in useEffect receives a courses object where their respective acronyms are and returns a string concatenated with the acronym of each course like this "LEIT - 1º Ano/LEE - 2º Ano"
below the function code:
function cursoSigla(cursos) {
const sigla = [];
if (cursos.length > 1) {
if (sigla.length !== cursos.length) {
cursos.map((curso) => sigla.push(`${curso.sigla} - ${curso.ano}º Ano`));
return sigla.join('/');
}
}
if (cursos.length === 1) {
sigla.push(cursos[0].sigla);
return `${sigla[0]} - ${cursos[0].ano}º Ano`;
}
console.log(sigla);
return '';
}
My question is how to introduce a line break in my table cell ?
Instead of rendering them as:
<TableCell align="right">{row.curso}</TableCell>
You could do something like this instead:
<TableCell>
{row.curso.split("/").map((curs, idx) => (
<p key={idx}>{curs}</p>
))}
</TableCell>

Textfield not working properly inside a table ReactJs

I have an object containing an array of objects
initialPreconfigTodoState = {
todos: [
{
title: 'title1',
dueDate: new Date(),
create: true,
assignedTo: 'role',
},
{
title: 'title2',
dueDate: new Date(),
create: true,
assignedTo: 'role',
}]
};
I use this array of object to control the state of a Textfield in a table
const [preconfig, setPreconfig] = useState(initialPreconfigTodoState);
{preconfig.todos.map((todo, index) => {
return (
<TableRow
className="h-64 cursor-pointer"
key={Math.random()}
>
<TableCell className="th" component="th" scope="row">
<TextField
id={`titleForm${index}`}
name={`titleForm${index}`}
onChange={(event) => handleTitle(event, index)}
value={todo.title}
type="text"
variant="outlined"
required={todo.create ? true : false}
fullWidth
/>
</TableCell>
when I try to type in the Textfield I don't know what happens but I need to click the Textfield for every character I type, I think is the useState hook that is causing this problem when re-rendering the component and I can't find a solution for this.
this is my handle function on the onChange Callback,
const handleTitle = (event, index) => {
let newArray = [...preconfig.todos];
newArray[index] = {
...newArray[index],
title: event.target.value,
};
setPreconfig({ todos: newArray });
};
here is the full code
const initialPreconfigTodoState = {
todos: [
{
title: "title1",
dueDate: new Date(),
create: true,
assignedTo: "Role"
},
{
title: "title2",
dueDate: new Date(),
create: true,
assignedTo: "Role"
}
]
};
function TodoDialog() {
const [preconfig, setPreconfig] =
useState(initialPreconfigTodoState);
const handleTitle = (event, index) => {
let newArray = [...preconfig.todos];
newArray[index] = {
...newArray[index],
title: event.target.value
};
setPreconfig({ todos: newArray });
};
return (
<div>
<Dialog open={true} maxWidth="xl" scroll="paper">
<DialogContent>
<div>
<Table aria-labelledby="tableTitle">
<TableBody>
<TableRow className="h-64 cursor-pointer" key=.
{Math.random()}>
<TableCell className="th" component="th" scope="row">
Title
</TableCell>
<TableCell className="th" component="th" scope="row">
Due Date
</TableCell>
<TableCell className="th" component="th" scope="row">
Asigned To
</TableCell>
<TableCell className="th" component="th" scope="row">
Create
</TableCell>
</TableRow>
{preconfig.todos.map((todo, index) => {
return (
<TableRow
className="h-64 cursor-pointer"
key={Math.random()}
>
<TableCell className="th" component="th" scope="row">
<TextField
id={`titleForm${index}`}
name={`titleForm${index}`}
onChange={(event) => handleTitle(event, index)}
value={todo.title}
type="text"
variant="outlined"
required={todo.create ? true : false}
fullWidth
/>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</DialogContent>
<DialogActions>
<Button color="primary" variant="contained">
Cancel
</Button>
<Button type="submit" color="primary" variant="contained" autoFocus>
Save
</Button>
</DialogActions>
</Dialog>
</div>
);
}
That's because the key={Math.random()} on TableRow.
Change it to key={index} and it should work.
With the Math.random() your keys changes every rendering and react loses its reference.
The issue is caused by thenuseState indeed. Your whole form (I suppose it's a form of some kind) changes the array from which it's mapped in render. No wonder the whole form re-renders.
The obvious solution would be to store the input values (eg in state) and update the original array (via setPreconfig) only on submit.

How can I show edit button in column cell while hovering over table row in Material UI Table?

Currently My table view is like following:
Mui Table The table has a column name 'Action' which has a edit icon button.
Now I want to show (visibility) edit icon to edit each row only when user will hover over table row.
I have tried to override MUITable theme of Material Table but the following code didn't work. can anybody help me?
const getMuiTheme = () => createMuiTheme({
overrides: {
MUIDataTableBodyCell: {
root: {
'&:last-child': {
visibility: 'hidden'
}
}
},
MuiTableRow: {
root: {
'&$hover:hover': {
'& .MUIDataTableBodyCell-root': {
'&:last-child': {
visibility: 'visible'
}
}
}
}
}
}
});
This is an example for that purpose: https://codesandbox.io/s/material-demo-hr3te?file=/demo.js.
Basically I do something:
Add new state to store a boolean variable to determine when to show/hide a component.
Add onMouseEnter and onMouseLeave to TableRow component, for hover effect.
Then set state according to the hover event above.
That's it!
You need to add one action column in your columns array like below:
const columns = [
{
name: 'id',
label: 'id',
options: {
sort: false,
viewColumns: false,
display: false,
filter: false
}
},
{
name: 'transportationBranch',
label: 'Transportation Branch',
options: {
sort: false,
viewColumns: false
}
},
{
name: 'charge',
label: 'Charge',
options: {
filter: false,
sort: false
}
},
{
name: 'tax',
label: 'Tax',
options: {
filter: false,
sort: false
}
},
{
name: '',
label: '',
options: {
filter: false,
sort: false,
viewColumns: false,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<IconButton
id={'Edit-' + tableMeta.rowIndex}
style={{display: 'none'}}
component="button"
variant="body2"
onClick={(event) => {
console.log(event);
alert(tableMeta.rowIndex);
}}
>
<EditIcon />
</IconButton>
);
}
}
}
];
add the the following option in mui datatable options:
setRowProps: (row, dataIndex, rowIndex) => {
return {
onMouseEnter: (e) => handleRowHover(e, row, rowIndex),
onMouseLeave: (e) => handleRowHoverLeave(e, row, rowIndex)
};
},
Write these two event in your component:
function handleRowHover(event, row, rowIndex) {
let control = document.getElementById('Edit-' + rowIndex);
control.style.display = 'block';
}
function handleRowHoverLeave(event, row, rowIndex) {
let control = document.getElementById('Edit-' + rowIndex);
control.style.display = 'none';
}
Note: we used id={'Edit-' + tableMeta.rowIndex} in IconButton so
that we can use getElementById in our two events.
If you can implement this properly then you will see this type ui on your browser:
I modified my original answer according to the mods comments:
I modified ShinaBR2 answer, so that it shows only a text in the current row: https://codesandbox.io/s/material-ui-table-onhover-action-zfloy?file=/demo.js
The idea is to use the row id on mouse enter (or mouse hover) and compare it with the current row, that is hovered to display the element in the row.
export default function SimpleTable() {
const classes = useStyles();
const [showActionId, setShowActionId] = useState(-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">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.name}
onMouseEnter={() => {
setShowActionId(row.id); // set id here
}}
onMouseLeave={() => setShowActionId(-1)}
>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">
{row.id === showActionId ? "Show me" : ""} // check the id here and display the message
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
One approach for this is css in js code , we make the icon hidden by default(hiddePin in makeStyles), then change the 'hidden' to 'visible' on hover using "&:hover $clearHidden".
below code was verified . my place is not good work with CodeSandBox(network issue) , so i just show the code here .
const useStyles = makeStyles({
table: {
minWidth: 350,
},
root: {
"&:hover $clearHidden": {
visibility: "visible"
}
},
clearHidden: {},
hiddePin: {
visibility: "hidden"
}
});
return (
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>name</TableCell>
<TableCell align="center">desc</TableCell>
<TableCell align="center">title2</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name} hover={true}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="center">{row.desc}</TableCell>
<TableCell align="center" className={classes.root} >
<IconButton size="small" className={clsx(classes.hiddePin, classes.clearHidden)} onClick={(event) => handlePinClick(event,row.stamp)}>
<SvgPinOutLine />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
To only show the actions in the row your hovering over, I added the following useState objects
const [showAction, setShowAction] = React.useState(false);
const [showId, setShowId] = React.useState(0);
and then in the row object
onMouseEnter={() => {
setShowAction(true);
setShowId(row.id);}
}
onMouseLeave={() => setShowAction(false)}
and then in the specific cell that shows the actions:
{row.id == showId && showAction ? (
<IconButton>
<DeleteIcon/>
</IconButton>
) : (
<Box></Box>)}
I hope this helps someone!

Resources