so I have this Table I've rendered its rows from an array map as shown below
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Object Name</TableCell>
<TableCell align="center">Object Field and Values</TableCell>
</TableRow>
</TableHead>
<TableBody>
{union &&
unionArray.map((row) => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
{Object.keys(row).map((key) =>
key === "name" ? (
""
) : (
<TableCell align="center">
{/*insert dropdown select*/}
<FormControl
variant="outlined"
className={classes.formControl}
>
<InputLabel htmlFor="outlined-age-native-simple">
Values
</InputLabel>
<Select
native
label="Value"
>
<option aria-label="None" value="" />
<option>{key}:{row[key]}</option>
</Select>
</FormControl>
</TableCell>
)
)}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
the array of objects where I mapped from is shown below. i.e UnionArray
my problem is the rows that have a third key/value pair are rendering as entire table cell, I just want them to be part of the dropdown. the output now is something like this
you could modify your array you get from the api and do sth like this:
let modifiedUnion = unionArray.map(el=>{
let tempObj={}
let values=[]
Object.keys(el).map(key=>
if(key!=="name"){
tempObj.values = values.push({[key]:el[key]})
}else{
tempObj.name = el.name
}
return tempObj
})
and after that write this part like this:
{modifiedUnion.map((row) => (
<TableRow key={row.name}>
<TableCell align="center">
{row.name}
</TableCell>
<TableCell align="center">
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel htmlFor="outlined-age-native-simple">
Values
</InputLabel>
<Select native label="Value">
<option aria-label="None" value="" />
{row.values.map((key) =>
<option>{Object.keys(key)[0]}:{Object.values(key)[0]}</option>
)}
</Select>
</FormControl>
</TableCell>
</TableRow>
))}
I don't have enough reputations to comment but the answer lies in #lakshya's response and #ahmed's comment. Object.keys will return null for when key === 'name' but it will return valid JSX for when the keys aren't name, hence, your table cell having 2 dropdowns.
As for how to go about formatting the response, you can format your loop like in the image attached.
Convert the object to a format like
[{
name: 'obj1',
values: [
{
a: 1
}
]
},
{
name: 'obj3',
values: [
{
c: 2,
d: 5
}
]
}
]
Related
I want to create a cell for each value of my array which is stored in a array of objects but what is going on is all the data which is in the array which is inside the object is not getting rendered as individual table cell. How to achieve
import {Paper,Table,TableBody,TableCell,TableContainer,TableHead,TableRow} from "#mui/material";
export function Schedule(prop){
let data = prop.data
console.log(data)
return(
<>
{!data ? "" :
<TableContainer>
{data.map((result) =>(
<Table>
<TableHead>
<TableRow>
<TableCell>Fact Name</TableCell>
<TableCell>{result.slotFree}</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell>
{result.name}
</TableCell>
<TableCell>
{result.status}
</TableCell>
</TableRow>
</TableBody>
</Table>
))}
</TableContainer>
}
</>
)
}[![This is my incoming data array of object](https://i.stack.imgur.com/sygfU.png)](https://i.stack.imgur.com/sygfU.png)
that result
I want that each free slot comes as an individual cell and should not render out in the same line This is the output](https://i.stack.imgur.com/jMTFC.png)
I want the time and slot status to be in the same column
Within this program, I'm trying to map a function across an Array of objects in order to render rows with information from this Array of users. When doing so, TypeScript is giving me a variety of issues when trying to access this information. I can't see what it is I'm missing that would allow me to access the properties.
The interfaces for my objects:
type TableData = Array<Row>;
interface Row {
id: string,
twilio_sid: string,
user_sid: string,
name: string,
activity_sid: string,
activity_name: string,
activity_last_updated: string,
role_id?: string,
queues?: string,
channels: [],
role?: string
}
Attempted map thus far:
{(rowsPerPage > 0
? Object.entries(rows).slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: rows
).map((user) => (
<TableRow key={user.id}>
<TableCell component="th" scope="row">
{user.name}
</TableCell>
<TableCell style={{ width: 160 }} align="left">
{user.role}
</TableCell>
<TableCell style={{ width: 160 }} align="left">
{user.activity_name}
</TableCell>
<TableCell style={{ width: 160 }} align="right">
Edit
</TableCell>
</TableRow>
))}
I previously was not using the Object.entries(obj) implementation but this still threw an error stating that slice was not a function on this type Row.
Does an implementation exist where I can still use slice in this regard?
Solution
Replacing Object.entries(obj) with Object.values(obj)
{(rowsPerPage > 0
? Object.values(users).slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: Object.values(users)
)
.map((user: User) => (
<TableRow key={user.id.toString()}>
<TableCell component="th" scope="row">
<p>{user.name.toString()}</p>
</TableCell>
<TableCell style={{ width: 160 }} align="left">
<p>{user.role?.toString()}</p>
</TableCell>
<TableCell style={{ width: 160 }} align="left">
<p>{user.activity_name.toString()}</p>
</TableCell>
<TableCell style={{ width: 160 }} align="right">
<p></p>
</TableCell>
</TableRow>
))}
The above implementation with Object.values(obj).slice() worked perfectly for my needs.
Object.entries returns an array of key-value pairs which are stored in arrays like [key, value]. What you'd probably want is the Object.values() function (which returns only the values, which you're interested in).
https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Object/values
I've got this table rendering this APP_DATA object. But I want to have the number of rows as a column (header left blank on purpose), how would i dynamically render the number next to each row?
bare in mind, since only "prt" and "cat" are arrays, you can only use Array methods on those.
i really appreciate any help
const API_DATA = [
{
"bnd": "test brand 11",
"url": "htttps://www.test.com/all-products11",
"cnt": "UK",
"prt": ['Perf. Supps'],
"cat": ['Keto']
},
{
"bnd": "test brand 12",
"url": "htttps://www.test.com/all-products12",
"cnt": "UK",
"prt": ['Diet. Supps'],
"cat": ['Gaming']
},
]
// table
return (
<>
<TableBody >
{filteredData(props.API_DATA,props.filters).map(row => (
<TableRow key={row.name}>
<TableCell style={{ textAlign:"left"}} align="center">{row.bnd}</TableCell>
<TableCell style={{ textAlign:"left"}} align="center">{row.bnd}</TableCell>
<TableCell style={{ textAlign:"left"}} align="center">{row.cnt}</TableCell>
<TableCell style={{ textAlign:"left"}} align="center">{row.prt.map((e) => <span style={{padding: "5px"}} key={e} >{`${e + ','}`}</span>)}</TableCell>
<TableCell style={{ textAlign:"left"}} align="center">{row.cat}</TableCell>
<TableCell style={{ textAlign:"left"}} align="center"><a href={row.url}>{row.url}</a></TableCell>
</TableRow>
))}
</TableBody>
</>
)
map method provide you the index as a second argument . since the index starts with 0 you can do index + 1 . so that the rownum starts with 1 .
<TableBody >
{filteredData(props.API_DATA,props.filters).map((row, index) => (
<TableRow key={row.name}>
<TableCell style={{ textAlign:"left"}} align="center">{index + 1}</TableCell>
.....
</TableRow>
))}
</TableBody>
Working Code :https://codesandbox.io/s/broken-https-2i454?file=/src/App.js
I'm using Material UI in my reactjs project and I'm trying the update the value entered inside a textfield of a table using onChange function for the textfield, currently the value is present inside an array of array,I want to update the componentQuantity , I'm aware of passing the event to get the event.target.value , but what is the correct way to update the specific value of the textfield present inside a array of array. Please someone help me out here.
class TabData extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
data: [
{
BomComponentCode: "345543",
BomComponentName: "COMP1",
BomComponentRefUOM: "gm",
rowId: 0,
consumptionBatchNumbers: [
{
componentBatchNumber: "20",
componentQuantity: 2
},
{
componentBatchNumber: "21",
componentQuantity: 3
}
]
},
//2nd cloumn
{
BomComponentCode: "5543",
BomComponentName: "COMP2",
BomComponentRefUOM: "KG",
rowId: 1,
consumptionBatchNumbers: [
{
componentBatchNumber: "22",
componentQuantity: 4
},
{
componentBatchNumber: "23",
componentQuantity: 5
}
]
}
],
renderState: false
};
}
handleUpdate = (index, Code) => {
this.setState({
renderState: Code
});
};
handleChange = (e) => {
console.log(e.target.value)
};
render() {
const { classes } = this.props;
const { data, renderState } = this.state;
return (
<div className={classes.list}>
<React.Fragment>
<Grid
item
xs={12}
sm={12}
md={12}
className={classes.grid}
style={{ paddingLeft: "0" }}
>
<p variant="h6" className={classes.font}>
Table Details
</p>
<span className={classes.borders}></span>
</Grid>
<div>
<TableContainer component={Paper} className={classes.paper}>
<Table className={classes.table} aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell> Number</TableCell>
<TableCell> Text</TableCell>
<TableCell>UOM</TableCell>
<TableCell> Batch </TableCell>
<TableCell> Quantity</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
<React.Fragment>
{data.map((item, i) => (
<React.Fragment>
<TableRow key={i}>
<TableCell scope="row" align="left">
{item.BomComponentCode}
</TableCell>
<TableCell align="left">
{item.BomComponentName}
</TableCell>
<TableCell align="left">
{item.BomComponentRefUOM}
</TableCell>
<TableCell>
{item.consumptionBatchNumbers.map((row, i) => (
<div key={i}>
<TableCell align="left">
{row.componentBatchNumber}
</TableCell>
</div>
))}
</TableCell>
{renderState === item.BomComponentCode ? (
<TableCell align="left">
{item.consumptionBatchNumbers.map((row, indi) => (
<div key={indi}>
<input
// value={row.componentQuantity}
onChange={(e) =>
this.handleChange(e)
}
defaultValue={row.componentQuantity}
/>
</div>
))}
</TableCell>
) : (
<TableCell align="left">
{item.consumptionBatchNumbers.map((row, indi) => (
<div key={indi}>
<TableCell align="left">
{row.componentQuantity}
</TableCell>
</div>
))}
</TableCell>
)}
<TableCell>
<button
onClick={() =>
this.handleUpdate(i, item.BomComponentCode)
}
>
Update
</button>
</TableCell>
</TableRow>
</React.Fragment>
))}
</React.Fragment>
</TableBody>
</Table>
</TableContainer>
</div>
<Grid container justify="center">
<Grid
item
xs={4}
sm={4}
md={2}
style={{
textAlign: "center",
padding: "1rem"
}}
>
<button >Close</button>
</Grid>
</Grid>
</React.Fragment>
</div>
);
}
}
export default (TabData);
To modify the value in the deeply nested array you need indexes . Once you have the indexes its easier to change the values .
Change the handleChange method to accept the row and deeply nested row's indexes.
onChange={(e) => this.handleChange(e, i, indi)}
Once you have the indexes we can deep clone the original state to create a new copy of state and mutate it directly .
handleChange = (e, rowIndex, consumptionBatchIndex) => {
// deep clone the data
const clonedData = JSON.parse(JSON.stringify(this.state.data));
clonedData[rowIndex].consumptionBatchNumbers[
consumptionBatchIndex
].componentQuantity = e.target.value;
this.setState({
data: clonedData
});
};
Now we can read the value in the input as
<input
value={row.componentQuantity}
onChange={(e) => this.handleChange(e, i, indi)}
/>
im creating an array for list cart items, works well, but wanted to know how can i access to each item properties to create the total order amount outside table item
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell className={classes.hideOnMobile}>
</TableCell>
<TableCell>PRODUCTO</TableCell>
<TableCell>PRECIO</TableCell>
<TableCell align="center">CANTIDAD</TableCell>
<TableCell align="right">TOTAL</TableCell>
<TableCell align="right"></TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map((row) => (
<Item data={row} {...props} />
))}
</TableBody>
<TableFooter className={classes.table}>
<TableCell>
</TableCell>
<TableCell align="right">TOTAL ORDER</TableCell>
<TableCell align="left">"TOTAL GOES HERE"</TableCell>
</TableFooter>
</Table>
This is my JSON data
0:
id: 31
image: "https://example.com/test.jpg"
name: "Product name 1"
price: 3
quantity: 13
total: 39
__proto__: Object
1: {id: 2, name: "Product name 2", price: 3, …}
length: 2
I would have a total variable somewhere in the component (a class property in this example, could very well just be a variable if you are using functional components), and accumulate the total as you loop over your data. That way, you only have to loop over your data set once. Something like this:
class App extends React.Component {
data = {/* your data */}
total = 0;
render() {
return (
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell className={classes.hideOnMobile}>
</TableCell>
<TableCell>PRODUCTO</TableCell>
<TableCell>PRECIO</TableCell>
<TableCell align="center">CANTIDAD</TableCell>
<TableCell align="right">TOTAL</TableCell>
<TableCell align="right"></TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.data.map((row) => (
this.total += row.total;
return <Item data={row} {...props} />
))}
</TableBody>
<TableFooter className={classes.table}>
<TableCell>
</TableCell>
<TableCell align="right">TOTAL ORDER</TableCell>
<TableCell align="left">{this.total}</TableCell>
</TableFooter>
</Table>
);
}