React issue mapping payload to table with children array - javascript

I am getting a payload and trying to map the data to a table. I am having difficulty with mapping because of the auditline array.
Does anyone know of way of flattening the auditline so that I can map my data correctly.
payload:
action:
id: 301
module : 2
name: "Create folder"
auditline:
0: Id: 1723
description:"Folder has been changed from Matlab to C#"
1: Id: 1724
description:"Folder name is Matlab"
This is the map function snippet.
<TableBody>
{data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map(n => {
const isSelected = this.isSelected(n.id);
return (
<TableRow
hover
onClick={event => this.handleClick(event, n.id)}
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
>
<TableCell>{n.action.name}</TableCell>
<TableCell>{n.auditLines.description}</TableCell>
<TableCell>
</TableCell>
</TableRow>
);
})}

You could map your array of audits to create multiple table cells :
<TableRow
hover
onClick={event => this.handleClick(event, n.id)}
role="checkbox"
aria-checked={isSelected}
tabIndex={-1}
key={n.id}
selected={isSelected}
>
<TableCell>{n.action.name}</TableCell>
{n.auditLines.map(audit => <TableCell key={audit.id}>{audit.description}</TableCell>)}
<TableCell>
</TableCell>
</TableRow>
Or, if you want all of them in a single one, use join

Related

i am trying to render Svg icons in a table , using material ui , in a react application. However, i see duplicate icons for different rows, any ideas?

I have an array of objects, where each object has a cryptocurrency-Id. i am using map to render a table and for rendering the token-image in the table i am calling a function: find_corresponding_icon(crypto-name), this function takes the crypto-name as a parameter and returns an image corresponding to it.I am seeing duplicate images rendering in my table.
import {ReactComponent as EthIcon} from '../../assets/eth.svg'
import {ReactComponent as DaiIcon} from '../../assets/dai.svg'
import {ReactComponent as UsdtIcon} from '../../assets/usdt.svg'
useEffect(()=>{
async function api_call(){
let res = await getTokenInfo()
setCurrecnyInfo(res.data.message) //setting state with an array of json objects
//currencyInfo=[{currencyId:1,tokenName:'Ethereum',Amount:312},
//{currencyId : 2, tokenName:'Dai',Amount:182},{currencyId : 3
, // tokenName:'USDT',Amount:882}]
}
api_call();
},[])
function find_corresponding_icon(CurrencyId){
if(CurrencyId === 1)
return <EthIcon />
else if(CurrencyId === 2)
return <DaiIcon />
else if(CurrencyId === 3)
return <UsdtIcon />
}
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>TokenName</TableCell>
<TableCell>Name</TableCell>
<TableCell>Amount($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row,id) => (
<TableRow key={id}>
<TableCell component="th" scope="row">{find_corresponding_icon(row.currencyId)}</TableCell>
<TableCell align="right">{row.tokenName}</TableCell>
<TableCell align="right">{row.Amount}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)
The result looks like so:
i am very sure all the icons look different, so there should not be a repetition

MUI: The `anchorEl` prop provided to the component is invalid

I m building a React 18.2 app using MUI 5.10.5, but I have run into a problem creating a <Menu /> element that opens in response to a button click. The menu appears, but it seems the anchorEl is not configured properly, because the menu appears in the top-left of the screen and the browser console has this complaint:
The issue is complicated by the fact that the menus pertain to each row in a <Table /> so, besides anything else, I am not sure if I am supposed to have a single menu outside the table, or repeat the menu for each row in the table. Which seems expensive. But I have organised the code so that the menu is currently duplicated.
export const MetricsQuery = () => {
const [menuState, setMenuState] = useState<{
open: boolean;
anchorEl: null | Element;
}>({open: false, anchorEl: null});
const handleOpenMenuClick = ({currentTarget}: MouseEvent) =>
setMenuState({open: true, anchorEl: currentTarget});
return (
<TableContainer component={Paper}>
<StyledTable>
{/*Header*/}
<TableBody>
{queryMetrics.map((row: QueryMetric) => (
<TableRow>
<TableCell component="th" scope="row">
{row.locationName}
</TableCell>
<TableCell>{row.deviceName}</TableCell>
<TableCell>{row.pointName}</TableCell>
<TableCell>
<Stack>
<IconButton
id="basic-button"
aria-controls={menuState.open ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={menuState.open ? 'true' : undefined}
onClick={handleOpenMenuClick}
>
<MoreVertIcon sx={{color: theme.palette.primary.light}} fontSize="small"/>
</IconButton>
<Menu
anchorEl={menuState.anchorEl}
open={menuState.open}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button'
}}
>
<MenuItem onClick={handleClose}>Remove</MenuItem>
<MenuItem onClick={handleClose}>Disable</MenuItem>
<MenuItem onClick={handleClose}>Pin to Top</MenuItem>
</Menu>
</Stack>
</TableCell>
</TableRow>
))}
</TableBody>
</StyledTable>
</TableContainer>
)
}
I have tried quite a number of configurations; such as
omitting the ID of the <IconButton />
making this ID unique for each row
having a single menu outside of the <StyledTable />
following the menu documentation more literally
Not sure what else to try..suggestions?
I am not sure if I am supposed to have a single menu outside the table, or repeat the menu for each row in the table.
You can have just one Menu outside of the table. The anchorEl will determine where it is positioned.
You can also have a Menu for every table row, as it will not be rendered to the DOM when it is closed. The advantage of this approach is that each menu can transition independently, so that one can animate closed while another animates open. But if you want to do it this way then you will need a separate menuState for each row, as they all have their own anchors.
What you have right now will open many copies of the same menu, all on top of each other, when any one of the buttons is clicked.
I would move the button and its menu into a new component so that you can have a separate state for each.
const RowMenu = ({ id }: QueryMetric) => {
const [menuState, setMenuState] = useState<{
open: boolean;
anchorEl: null | Element;
}>({ open: false, anchorEl: null });
const handleOpenMenuClick = ({ currentTarget }: { currentTarget: Element }) =>
setMenuState({ open: true, anchorEl: currentTarget });
const handleClose = () => {
setMenuState((prevState) => ({ ...prevState, open: false }));
};
const menuId = `basic-menu-${id}`;
const buttonId = `basic-button-${id}`;
return (
<>
<IconButton
id={buttonId}
aria-controls={menuState.open ? menuId : undefined}
aria-haspopup="true"
aria-expanded={menuState.open ? "true" : undefined}
onClick={handleOpenMenuClick}
sx={{ alignSelf: "center" }}
>
<MoreVertIcon
sx={{ color: (theme) => theme.palette.primary.light }}
fontSize="small"
/>
</IconButton>
<Menu
id={menuId}
anchorEl={menuState.anchorEl}
open={menuState.open}
onClose={handleClose}
MenuListProps={{
"aria-labelledby": buttonId
}}
>
<MenuItem onClick={handleClose}>Remove</MenuItem>
<MenuItem onClick={handleClose}>Disable</MenuItem>
<MenuItem onClick={handleClose}>Pin to Top</MenuItem>
</Menu>
</>
);
};
export const MetricsQuery = () => {
return (
<TableContainer component={Paper}>
<StyledTable>
{/*Header*/}
<TableBody>
{queryMetrics.map((row) => (
<TableRow key={row.id}>
<TableCell component="th" scope="row">
{row.locationName}
</TableCell>
<TableCell>{row.deviceName}</TableCell>
<TableCell>{row.pointName}</TableCell>
<TableCell>
<Stack>
<RowMenu {...row} />
</Stack>
</TableCell>
</TableRow>
))}
</TableBody>
</StyledTable>
</TableContainer>
);
};
CodeSandbox Link
making this ID unique for each row
If you have ids in your DOM then they need to be unique. Your aria attributes don't make sense if they are all the same. But this is a separate issue and I don't think that this would cause any of the issues that you are having with anchor elements.

React function is always called with same parameter in a loop

I have a data array and display its elements in table rows. The last table cell includes a menu item which calls a function with parameter.
The problem is that function handleOpenApproveDialog is always fired with same data element, the last element of myData array.
CodeSandbox link
const handleOpenApproveDialog = (ev, item) => {
setAnchorEl(null);
dispatch(openApproveDialog(item));
//* problem: incoming item parameter is always same. The last element of myData array.
};
<TableBody>
{myData.map((n, i) => {
return (
<TableRow
tabIndex={-1}
key={i}
onClick={(event) => handleRowClick(event, n)}
>
<TableCell className="p-4 md:p-16" component="th" scope="row">
{n.company}
</TableCell>
<TableCell
className="p-4 md:p-16"
component="th"
scope="row"
align="right"
>
<IconButton
aria-label="actions"
id="actions-button"
aria-controls={isMenuOpen ? "actions-menu" : undefined}
aria-expanded={isMenuOpen ? "true" : undefined}
aria-haspopup="true"
onClick={(ev) => {
handleMenuClick(ev);
ev.stopPropagation();
}}
>
<MoreVertIcon />
</IconButton>
<Menu
id="actions-menu"
anchorEl={anchorEl}
open={isMenuOpen}
onClose={handleMenuClose}
>
<MenuItem
onClick={(ev) => {
handleOpenApproveDialog(ev, n);
ev.stopPropagation();
}}
>
Approve
</MenuItem>
</Menu>
</TableCell>
</TableRow>
);
})}
</TableBody>

How do I map over data from API in a collapsible table?

I don't know if the question captures what I had in my but I will explain below...
I fetched data from API and mapped into a collapsible table. Full details of the data should be embedded in EACH row such that onclick on each row, reveals the full details. Here's the code below
function Encounter() {
const [open2, setOpen2] = useState(false);
const [details, setDetails] = useState([]);
const getDetails = async () => {
try {
const fetch = await Axios.get(
"https://pshs3.herokuapp.com/all/encounter"
);
setDetails(fetch.data.data)
} catch (err) {
console.log(err);
}
};
useEffect(() => {
getDetails();
}, []);
return (
<Wrapper>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<TableCell/>
<TableCell >
Enrollment ID
</TableCell>
<TableCell >
Encounter
</TableCell>
<TableCell>
Facility Code
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{details.map((detail, idx) => {
return (
<>
<TableRow sx={{ "& > *": { borderBottom: "unset" } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton>
</TableCell>
<TableCell key={idx}>
{detail.enrollment_id}
</TableCell>
<TableCell key={idx}>
{detail.encounter}
</TableCell>
<TableCell key={idx}>
{detail.faciity_code}
</TableCell>
</TableRow>
<TableRow>
<TableCell
style={{ paddingBottom: 0, paddingTop: 0 }}
colSpan={6}
>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1 }}>
<Typography variant="h6" gutterBottom component="div">
Details
</Typography>
<Tooltip />
</Box>
</Collapse>
</TableCell>
</TableRow>
</>
);
})}
</TableBody>
</Table>
</Wrapper>
);
}
export default Encounter;
The problem I have is how to implement the open and setOpen state to individual row, also the Tooltip component(which is a the full table details from the API) to display full details of each row onclick which should correspond to the selected row in question.
Here are two solutions to the first problem.
1. Create a separate component for each <TableRow />
This component will have its own state and allows you to collapse/expand each row individually.
2. Use a dictionary for the open state
Since you have multiple (dynamic) rows, you can introduce a dictionary for the open state.
const [open, setOpen] = useState({});
For each row, you will use the open[idx] property to determine if the row should be "open"
<Collapse in={open[idx]} timeout="auto" unmountOnExit>
And in the <IconButton /> component, set the state based on the current row state.
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(current => ({ ...current, [idx]: !current[idx] }))}
>
Firstly add a variable in your details object:
const getDetails = async () => {
try {
const fetch = await Axios.get(
"https://pshs3.herokuapp.com/all/encounter"
);
const response = fetch.data.data;
response.map((elem) => elem.open = false)
setDetails(response)
} catch (err) {
console.log(err);
}
};
Then you can change the open variable for each element in details:
<IconButton
aria-label="expand row"
size="small"
onClick={() => detail.open = !detail.open)}
>
You might need to update the state, so change your onClick:
onClick={() => changeOpenStatus(idx))}
and the function:
const changeOpenStatus = (idx) => {
const newDetails = {...details}
newDetails[idx].open = !newDetails[idx].open;
setDetails(newDetails)
}

Javascript sort table inside map

I would like to sort some data that I am sending via "tableCells" object, using props.
const tableColumns = [
'Número do Bloco',
'Andar do apartamento',
'Número do apartamento',
'Tamanho do apartamento',
'Número da vaga',
'Ações',
];
Getting all the cells information via map, as follow.
How could I sort each column triggered by user click?
<TableRow>
{props.tableCells
? props.tableCells.map((tableCellContent) => (
<TableCell
align='center'
className={classes.tableCellAdjustment}
>
{document[tableCellContent]}
</TableCell>
))
: ''}
<TableCell
align='center'
className={classes.tableCellAdjustment}
>
<Fab
color='default'
aria-label='edit'
size='small'
className={classes.marginAdjustment}
>
<EditIcon />
</Fab>
<Fab
color='default'
aria-label='delete'
size='small'
className={classes.marginAdjustment}
>
<DeleteIcon
//onClick={() => this.props.deleteLine(document)}
/>
</Fab>
</TableCell>
</TableRow>

Categories

Resources