Related
I'm creating an app in react where if i click on a particular date or select the range of the date so the data should show of that date in a below table. if there is no date is selected that there will be zero record in that table. only when the date is selected than the record of that date or date range show in the table BUT I'M FAILED TO DO THAT IF ANYONE KNOWS HOW CAN I SOLVE THAT ISSUE THAN PLEASE HELP ME.... and theres one more issue here i'm using static data or limited no. of that, is there any way that i can create a dynamic data for the columns that i'm using in table so when i select any date than the data of that date should show in that table....
import { useEffect, useRef, useState } from "react";
import { DateRange } from "react-date-range";
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 format from "date-fns/format";
import { addDays } from "date-fns";
import "react-date-range/dist/styles.css";
import "react-date-range/dist/theme/default.css";
function createData(id, fromDate, toDate, name, calories, fat, carbs, protein) {
return { id, fromDate, toDate, name, calories, fat, carbs, protein };
}
const rows = [
createData(1, "22/11,2022", "ytuy", 159, 6, 2, 4),
createData(2, "23/11,2022", "Froz", 15, 6, 4, 3),
createData(3, "24/11,2022", "jhg", 19, 4, 14, 0),
createData(4, "25/11,2022", "gfj", 59, 3, 34, 1),
createData(5, "26/11,2022", "ytyu", 1, 2, 24, 4),
];
const DateRangeComp = () => {
const [range, setRange] = useState([
{
startDate: new Date(),
endDate: addDays(new Date(), 7),
key: "selection",
},
]);
const [open, setOpen] = useState(false);
const refOne = useRef(null);
useEffect(() => {
document.addEventListener("keydown", hideOnEscape, true);
document.addEventListener("click", hideOnClickOutside, true);
}, []);
const hideOnEscape = (e) => {
if (e.key === "Escape") {
setOpen(false);
}
};
const hideOnClickOutside = (e) => {
if (refOne.current && !refOne.current.contains(e.target)) {
setOpen(false);
}
};
return (
<div className="calendarWrap" style={{ marginBottom: "36px" }}>
<input
style={{ width: "15.9rem" }}
value={`${format(range[0].startDate, "MM/dd/yyyy")} to ${format(
range[0].endDate,
"MM/dd/yyyy"
)}`}
readOnly
className="inputBox"
onClick={() => setOpen((open) => !open)}
/>
<div ref={refOne}>
{open && (
<DateRange
onChange={(item) => setRange([item.selection])}
editableDateInputs={true}
moveRangeOnFirstSelection={false}
ranges={range}
months={1}
direction="horizontal"
className="calendarElement"
/>
)}
</div>
<>
<TableContainer component={Paper} style={{ marginTop: "30px" }}>
<Table
style={{ width: "100%" }}
size="small"
aria-label="a dense table"
>
<TableHead>
<TableRow>
<TableCell>S.No.</TableCell>
<TableCell align="center">From Date</TableCell>
<TableCell align="center">Dessert (100g serving)</TableCell>
<TableCell align="center">Calories</TableCell>
<TableCell align="center">Fat (g)</TableCell>
<TableCell align="center">Carbs (g)</TableCell>
<TableCell align="center">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.id}
</TableCell>
<TableCell align="center">{row.fromDate}</TableCell>
<TableCell align="center">{row.name}</TableCell>
<TableCell align="center">{row.calories}</TableCell>
<TableCell align="center">{row.fat}</TableCell>
<TableCell align="center">{row.carbs}</TableCell>
<TableCell align="center">{row.protein}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
</div>
);
};
export default DateRangeComp;
I am using material UI Collapsible Table: https://material-ui.com/components/tables/#table
I want to show dynamic data in the head and rows and want to repeat the head and rows so that if i click on any row(dynamic) it collapses.
I want to show data from this api in the table: http://wms-api.martoo.com/api/wms-orders
Currently the static data is showing but I am unable to show dyamic data from redux store.
import React, {useEffect} from 'react';
import { connect } from "react-redux";
import PropTypes from 'prop-types';
import {homeActions} from '../../store/home/actions'
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 CollapsibleTable = (props) => {
useEffect(() => {
props.allOrders();
}, []);
let AllOrdersresult = props.allOrdersState.map(item => ({
order_id: item.order.order_id,
net_total: item.order.net_total,
tax_total: item.order.tax_total,
date_created: item.order.date_created.split(' ')[0],
shipping_total: item.order.shipping_total,
product_qty: item.product_qty,
first_name: item.customer.first_name,
last_name: item.customer.last_name,
email: item.customer.email,
}));
const rows = [
{
name:'shafiq',
calories:'test',
fat:'test fat',
carbs:'test carbs',
protein:'test protein',
price:2000,
history: [
{ 'date': '2020-01-05', 'customerId': '11091700', 'amount': 3 },
{ 'date': '2020-01-02', 'customerId': 'Anonymous', 'amount': 1 },
],
},
{
name:'Mazhar',
calories:'test',
fat:'test fat',
carbs:'test carbs',
protein:'test protein',
price:2000,
history: [
{ 'date': '2020-01-05', 'customerId': '11091700', 'amount': 3 },
{ 'date': '2020-01-02', 'customerId': 'Anonymous', 'amount': 1 },
],
},
{
name:'Hanan Khan',
calories:'test',
fat:'test fat',
carbs:'test carbs',
protein:'test protein',
price:2000,
history: [
{ 'date': '2020-01-05', 'customerId': '11091700', 'amount': 3 },
{ 'date': '2020-01-02', 'customerId': 'Anonymous', 'amount': 1 },
],
}
];
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell />
<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) => (
<Row key={row.name} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
const mapStateToProps = state => {
return {
allOrdersState:state.homeReducer.allOrders,
};
};
const mapDispatchToProps = {
allOrders:homeActions.allOrders,
// allStocks:homeActions.allStocks,
// allUsers:homeActions.allUsers
};
const connectCollapsibleTable = connect(
mapStateToProps,
mapDispatchToProps
)(CollapsibleTable);
export { connectCollapsibleTable as CollapsibleTable };
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
enter image description here
I have solved the problem. since i am populating data from redux store so I take an empty array, then i take for loop and pushed objects inside array and showed the appropriate object items in the concerned tableCells.
Kindly have a look at my CollapsibleTable Component and compare this with MaterialUI collapsible tables then you would have better idea how to populate dynamic data in the table rows.
import React, {useEffect} from 'react';
import { connect } from "react-redux";
import PropTypes from 'prop-types';
import {homeActions} from '../../store/home/actions'
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';
import Button from '#material-ui/core/Button';
const useRowStyles = makeStyles({
root: {
'& > *': {
borderBottom: 'unset',
},
},
});
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.order_id}</TableCell>
<TableCell align="right">{row.date_created}</TableCell>
<TableCell align="right">{row.status}</TableCell>
<TableCell align="right">{row.total_sales}</TableCell>
<TableCell align="right">
<Button variant="contained" color="primary">Order Again </Button>
</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 CollapsibleTable = (props) => {
useEffect(() => {
props.allOrders();
},[]);
const ordersTest = [];
for (let i = 0; i < props.allOrdersState.length && props.allOrdersState.length; i++) {
ordersTest.push({
order_id: props.allOrdersState.length ? props.allOrdersState[0]['order']['order_id'] :'',
total_sales: props.allOrdersState.length ? props.allOrdersState[0]['order']['total_sales'] :'',
status: props.allOrdersState.length ? props.allOrdersState[0]['order']['status'] :'',
tax_total:props.allOrdersState.length ? props.allOrdersState[0]['order']['tax_total'] :'',
date_created: props.allOrdersState.length ? props.allOrdersState[0]['order']['date_created'] :'',
shipping_total: props.allOrdersState.length ? props.allOrdersState[0]['order']['shipping_total'] :'',
product_qty: props.allOrdersState.length ? props.allOrdersState[0]['product_qty'] :'',
first_name: props.allOrdersState.length ? props.allOrdersState[0]['customer']['first_name'] :'',
last_name: props.allOrdersState.length ? props.allOrdersState[0]['customer']['last_name'] :'',
email: props.allOrdersState.length ? props.allOrdersState[0]['customer']['email'] :'',
history: [
{ date: '2020-01-05', customerId: '11091700', amount: 3 },
{ date: '2020-01-02', customerId: 'Anonymous', amount: 1 },
],
});
}
console.log('all orders for each....', ordersTest);
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead style={{background:'#f4511e'}}>
<TableRow>
<TableCell />
<TableCell>Order No.</TableCell>
<TableCell align="right">Date</TableCell>
<TableCell align="right">Status</TableCell>
<TableCell align="right">Total</TableCell>
<TableCell align="right">Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ordersTest.slice(0, 10).map((row) => (
<Row key={row.order_id} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
const mapStateToProps = state => {
return {
allOrdersState:state.homeReducer.allOrders,
};
};
const mapDispatchToProps = {
allOrders:homeActions.allOrders,
// allStocks:homeActions.allStocks,
// allUsers:homeActions.allUsers
};
const connectCollapsibleTable = connect(
mapStateToProps,
mapDispatchToProps
)(CollapsibleTable);
export { connectCollapsibleTable as CollapsibleTable };
I've a react Table with multiple rows and 3 columns, every row has an edit button, when user clicks on the edit button of any row, i want the name data from that row to appear in the input textfield above.
I've tried Ref and setting up the state but none of them are working, please help.
Also, I've have a react-select field as well so please tell me if the same solution should apply to that?
Here is the full code https://codesandbox.io/s/naughty-nobel-xboze
import React, { useState, useRef } from "react";
import TextField from "#material-ui/core/TextField";
import {
Table,
TableCell,
TableBody,
TableHead,
TableRow
} from "#material-ui/core";
const data = [
{
id: 1,
name: "tom1"
},
{
id: 2,
name: "tom2"
},
{
id: 3,
name: "tom3"
},
{
id: 4,
name: "mike"
}
];
const Demo = () => {
const key = useRef(null);
const [word, setWord] = useState("");
const focusText = (name) => {
key.current.value = name;
};
return (
<React.Fragment>
<TextField
style={{ width: "100%" }}
id="outlined-basic"
variant="outlined"
value={word}
size="small"
color="primary"
ref={key}
onChange={e => {
setWord(e.target.value);
}}
/>
<Table>
<TableHead>
<TableRow>
<TableCell>id</TableCell>
<TableCell>name</TableCell>
<TableCell>action</TableCell>
</TableRow>
</TableHead>
<TableBody>{data.map(item => (
<TableRow>
<TableCell align="left">
<p style={{ fontSize: "13px", margin: "0px" }}>{item.id}</p>
</TableCell>
<TableCell align="left">
<p style={{ fontSize: "13px", margin: "0px" }}>{item.name}</p>
</TableCell>
<TableCell>
<button onClick={() => focusText(item.name)}>edit</button>
</TableCell>
</TableRow>
))}</TableBody>
</Table>
</React.Fragment>
);
};
export default Demo;
Sandbox link https://codesandbox.io/s/eloquent-bardeen-972th
import React, { useState, useRef } from "react";
import TextField from "#material-ui/core/TextField";
import {
Table,
TableCell,
TableBody,
TableHead,
TableRow
} from "#material-ui/core";
const data = [
{
id: 1,
name: "tom1"
},
{
id: 2,
name: "tom2"
},
{
id: 3,
name: "tom3"
},
{
id: 4,
name: "mike"
}
];
const Demo = () => {
const key = useRef(null);
const [word, setWord] = useState("");
const focusText = (id, name) => {
setWord(name);
};
return (
<>
<TextField
style={{ width: "100%" }}
id="outlined-basic"
variant="outlined"
value={word}
size="small"
color="primary"
ref={key}
onChange={e => {
setWord(e.target.value);
}}
/>
<Table>
<TableHead>
<TableRow>
<TableCell>id</TableCell>
<TableCell>name</TableCell>
<TableCell>action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{
data.map(item => (
<TableRow key={item.id}>
<TableCell align="left">
<p style={{ fontSize: "13px", margin: "0px" }}>{item.id}</p>
</TableCell>
<TableCell align="left">
<p style={{ fontSize: "13px", margin: "0px" }}>{item.name}</p>
</TableCell>
<TableCell>
<button onClick={() => focusText(item.id, item.name)}>edit</button>{" "}
</TableCell>
</TableRow>
))
}</TableBody>
</Table>
</>
);
};
export default Demo;
I am trying to develop a simple React app, with crud operations, but I can't even list my objects, which are stored into a sql database. I followed this example for the frontend: https://github.com/only2dhir/react-js-example
and I continued it, adding the component for my objects.
In my application, I have doctors and patients. A patient is assigned to a doctor, so a doctor can have one or more patients. In order to do this, I create my jsx file, ListCaregiverComponent.jsx, where I did that :
import React, { Component } from 'react'
import ApiServiceCaregiver from "../../service/ApiServiceCaregiver";
import ApiServicePatient from "../../service/ApiServicePatient";
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 Typography from '#material-ui/core/Typography';
class ListCaregiverComponent extends Component {
constructor(props) {
super(props)
this.state = {
caregivers: [],
patients: [],
message: null
}
this.deleteCaregiver = this.deleteCaregiver.bind(this);
this.editCaregiver = this.editCaregiver.bind(this);
this.addCaregiver = this.addCaregiver.bind(this);
this.reloadCaregiverList = this.reloadCaregiverList.bind(this);
this.reloadPatientList = this.reloadPatientList.bind(this);
}
componentDidMount() {
this.reloadCaregiverList();
this.reloadPatientList();
}
reloadCaregiverList() {
ApiServiceCaregiver.fetchCaregivers()
.then((res) => {
this.setState({caregivers: res.data.result})
});
}
reloadPatientList() {
ApiServicePatient.fetchPatients()
.then((res) => {
this.setState({patients: res.data.result})
});
}
deleteCaregiver(userId) {
ApiServiceCaregiver.deleteCaregiver(userId)
.then(res => {
this.setState({message : 'User deleted successfully.'});
this.setState({caregivers: this.state.caregivers.filter(user => user.id !== userId)});
})
}
editCaregiver(id) {
window.localStorage.setItem("userId", id);
this.props.history.push('/edit-caregiver');
}
addCaregiver() {
window.localStorage.removeItem("userId");
this.props.history.push('/add-caregiver');
}
render() {
return (
<div>
<br></br>
<br></br>
<Typography variant="h4" style={style}>Caregiver Details</Typography>
<br></br>
<br></br>
<Table>
<TableHead>
<TableRow>
<TableCell>Id</TableCell>
<TableCell align="right">Name</TableCell>
<TableCell align="right">Birth Date</TableCell>
<TableCell align="right">Gender</TableCell>
<TableCell align="right">Address</TableCell>
<TableCell align="center">Patients</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.caregivers.map(({ id, name, birthDate, gender, address, patients = [] }) =>(
<TableRow key={id}>
<TableCell component="th" scope="row">
{id}
</TableCell>
<TableCell align="right">{name}</TableCell>
<TableCell align="right">{birthDate}</TableCell>
<TableCell align="right">{gender}</TableCell>
<TableCell align="right">{address}</TableCell>
<TableCell align="right">
<TableRow>
<TableCell align = "right" >Id</TableCell>
<TableCell align = "right" >Name</TableCell>
<TableCell align="right">Birth Date</TableCell>
<TableCell align="right">Gender</TableCell>
<TableCell align="right">Address</TableCell>
<TableCell align="right">Medical Record</TableCell>
</TableRow>
{patients.map(({ id, name, birthDate, gender, address, medicalRecord })=> {
return (
<TableRow key={id}>
<TableCell component="th" scope="row">
{id}
</TableCell>
<TableCell align="right">{name}</TableCell>
<TableCell align="right">{birthDate}</TableCell>
<TableCell align="right">{gender}</TableCell>
<TableCell align="right">{address}</TableCell>
<TableCell align="right">{medicalRecord}</TableCell>
</TableRow>
)
})}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
}
const style ={
display: 'flex',
justifyContent: 'center'
}
export default ListCaregiverComponent;
The ListPatientComponent.jsx looks like this :
import React, { Component } from 'react'
import ApiServicePatient from "../../service/ApiServicePatient";
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 Typography from '#material-ui/core/Typography';
class ListPatientComponent extends Component {
constructor(props) {
super(props)
this.state = {
patients: [],
message: null
}
this.deletePatient = this.deletePatient.bind(this);
this.editPatient = this.editPatient.bind(this);
this.addPatient = this.addPatient.bind(this);
this.reloadPatientList = this.reloadPatientList.bind(this);
}
componentDidMount() {
this.reloadPatientList();
}
reloadPatientList() {
ApiServicePatient.fetchPatients()
.then((res) => {
this.setState({patients: res.data.result})
});
}
deletePatient(userId) {
ApiServicePatient.deletePatient(userId)
.then(res => {
this.setState({message : 'User deleted successfully.'});
this.setState({patients: this.state.patients.filter(user => user.id !== userId)});
})
}
editPatient(id) {
window.localStorage.setItem("userId", id);
this.props.history.push('/edit-patient');
}
addPatient() {
window.localStorage.removeItem("userId");
this.props.history.push('/add-patient');
}
render() {
return (
<div>
<br></br>
<br></br>
<Typography variant="h4" style={style}>Patient Details</Typography>
<br></br>
<br></br>
<Table>
<TableHead>
<TableRow>
<TableCell>Id</TableCell>
<TableCell align="right">Name</TableCell>
<TableCell align="right">Birth Date</TableCell>
<TableCell align="right">Gender</TableCell>
<TableCell align="right">Address</TableCell>
<TableCell align="right">Medical Record</TableCell>
<TableCell align="center">Medication Plans</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.patients.map(row => (
<TableRow key={row.id}>
<TableCell component="th" scope="row">
{row.id}
</TableCell>
<TableCell align="right">{row.name}</TableCell>
<TableCell align="right">{row.birthDate}</TableCell>
<TableCell align="right">{row.gender}</TableCell>
<TableCell align="right">{row.address}</TableCell>
<TableCell align="right">{row.medicalRecord}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
}
const style ={
display: 'flex',
justifyContent: 'center'
}
export default ListPatientComponent;
and it works.
I also added two new js files, ApiServicePatient.js and ApiServiceCaregiver.js :
import axios from 'axios';
const CAREGIVER_API_BASE_URL = 'http://localhost:8080/caregivers';
class ApiServiceCaregiver {
fetchCaregivers() {
return axios.get(CAREGIVER_API_BASE_URL);
}
fetchCaregiverById(caregiverId) {
return axios.get(CAREGIVER_API_BASE_URL + '/' + caregiverId);
}
deleteCaregiver(caregiverId) {
return axios.delete(CAREGIVER_API_BASE_URL + '/' + caregiverId);
}
addCaregiver(caregiver) {
return axios.post(""+CAREGIVER_API_BASE_URL, caregiver);
}
editCaregiver(caregiver) {
return axios.put(CAREGIVER_API_BASE_URL + '/' + caregiver.id, caregiver);
}
}
export default new ApiServiceCaregiver();
import axios from 'axios';
const PATIENT_API_BASE_URL = 'http://localhost:8080/patients';
class ApiServicePatient {
fetchPatients() {
return axios.get(PATIENT_API_BASE_URL);
}
fetchPatientById(userId) {
return axios.get(PATIENT_API_BASE_URL + '/' + userId);
}
deletePatient(userId) {
return axios.delete(PATIENT_API_BASE_URL + '/' + userId);
}
addPatient(user) {
return axios.post(""+PATIENT_API_BASE_URL, user);
}
editPatient(user) {
return axios.put(PATIENT_API_BASE_URL + '/' + user.id, user);
}
}
export default new ApiServicePatient();
Also, in App.js, I added :
import React from 'react';
import './App.css';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import ListUserComponent from "./component/user/ListUserComponent";
import AddUserComponent from "./component/user/AddUserComponent";
import EditUserComponent from "./component/user/EditUserComponent";
import ListPatientComponent from "./component/patient/ListPatientComponent";
import ListCaregiverComponent from "./component/caregiver/ListCaregiverComponent";
function App() {
return (
<div className="container">
<Router>
<div className="col-md-6">
<h1 className="text-center" style={style}>React User Application</h1>
<Switch>
<Route path="/" exact component={ListUserComponent} />
<Route path="/users" component={ListUserComponent} />
<Route path="/add-user" component={AddUserComponent} />
<Route path="/edit-user" component={EditUserComponent} />
<Route path="/patients" component={ListPatientComponent} />
<Route path="/caregivers" component={ListCaregiverComponent} />
</Switch>
</div>
</Router>
</div>
);
}
const style = {
color: 'red',
margin: '10px'
}
export default App;
It doesn't work, it only says :
Unhandled Rejection (TypeError): Cannot read property 'map' of undefined
and it indicates the lines with :
<TableCell align="right">{row.address}</TableCell>
{row.patients.map(row => (
In the backend application, the class Caregiver has a list of patients objects, mapped as one-to-many :
#OneToMany(mappedBy = "caregiver", fetch = FetchType.EAGER)
private List<Patient> patients;
Does anyone have some suggestions?
--------UPDATE AFTER FIRST ANSWERS---------
I no longer get that error, but I can't display in my table of caregivers the assigned patients. The corresponding TableCell s are empty for the patients of each caregiver. Why is happening this?
You do this.state.caregivers.map(row => and then {row.patients.map(row =>, but I don't see any indication that each caregivers element has a patients array property
Since you are using asynchronous call the javascript start rendering and for a few miliseconds "this.state.caregivers" has no value so you cannot map a null value it needs to be array null.
Make sure to add a condition to check first or declare "this.state.caregivers" as empty array in the constructor.
As mentioned in other answers, patients is maybe undefined for some rows, so you could assign it a default value using Destructuring:
{this.state.caregivers.map(({ id, name, birthDate, gender, address, patients = [] }) => (
<TableCell component="th" scope="row">
{id}
</TableCell>
<TableCell align="right">{name}</TableCell>
<TableCell align="right">{birthDate}</TableCell>
<TableCell align="right">{gender}</TableCell>
<TableCell align="right">{address}</TableCell>
{patients.map(row => (
...
Try to add before each map the property you want to map and && like this:
this.state.caregivers && this.state.caregivers.map
Do this for each map in your code.
I would like to implement Google's Material UI Menu Item inside of a TableCell, as shown in their docs here, as seen below:
Here is my current approach:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import {
Grid,
IconButton,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Paper,
Menu,
MenuItem,
Button,
} from '#material-ui/core';
import { ExpandLess, ExpandMore } from '#material-ui/icons';
const styles = theme => ({});
const Questions = ({ data, classes, openMenu, anchorEls, handleClose }) => {
const CustomTableCell = withStyles(theme => ({
head: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
body: {
fontSize: 14,
},
}))(TableCell);
const formatData = rawData => Object.keys(rawData).map(key => rawData[key]);
const n = { key: 'hi', rating: 55, text: 'wassup' };
return (
<Grid container alignItems={'center'} direction={'column'} spacing={8}>
<Paper className={classes.root}>
<Button
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={e => openMenu('dude', e)}
>
<ExpandMore />
</Button>
<Menu
id={`dude`}
key="menu"
anchorEl={anchorEls.dude}
open={Boolean(anchorEls.dude)}
onClose={e => handleClose('dude', e)}
>
<MenuItem onClick={e => handleClose('dude', e)}>Delete</MenuItem>
<MenuItem onClick={e => handleClose('dude', e)}>Flag</MenuItem>
<MenuItem onClick={e => handleClose('dude', e)}>
Mark Answered
</MenuItem>
</Menu>
<Table className={classes.table}>
<TableHead>
<TableRow>
<CustomTableCell>Question</CustomTableCell>
<CustomTableCell numeric>Rating</CustomTableCell>
<CustomTableCell>Upvote</CustomTableCell>
<CustomTableCell>Downvote</CustomTableCell>
<CustomTableCell>Options</CustomTableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow className={classes.row} key={n.key}>
<CustomTableCell component="th" scope="row">
{n.text}
</CustomTableCell>
<CustomTableCell numeric>{n.rating}</CustomTableCell>
<CustomTableCell>
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => ''}
>
<ExpandLess />
</IconButton>
</CustomTableCell>
<CustomTableCell>
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => ''}
>
<ExpandMore />
</IconButton>
</CustomTableCell>
<CustomTableCell>
<Button
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={e => openMenu(n.key, e)}
>
<ExpandMore />
</Button>
<Menu
id={`simple-menu-${n.key}`}
key="menu"
anchorEl={anchorEls[n.key]}
open={Boolean(anchorEls[n.key])}
onClose={e => handleClose(n.key, e)}
>
<MenuItem onClick={e => handleClose(n.key, e)}>
Delete
</MenuItem>
<MenuItem onClick={e => handleClose(n.key, e)}>dude</MenuItem>
<MenuItem onClick={e => handleClose(n.key, e)}>choc</MenuItem>
</Menu>
</CustomTableCell>
</TableRow>
</TableBody>
</Table>
</Paper>
</Grid>
);
};
Questions.propTypes = {
data: PropTypes.object.isRequired,
anchorEls: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired,
openMenu: PropTypes.func.isRequired,
handleClose: PropTypes.func.isRequired,
};
Questions.defaultProps = {};
export default withStyles(styles)(Questions);
This is working, however the menu is not appearing in the correct place, even when passing in the related event e. I put in a dummy element before the table to test whether or not the table was affecting the menu, and found that the dummy worked just fine, as you can see in the screenshot below.
And the improperly placed menu from the button in the table:
Any ideas on what could be happening? Obviously, the context and anchorEl isn't correctly identifying the location on the page, but not sure what to do to combat that.
Problem in your openMenu function.You need to set the event target in your openMenu function, and set null in handleClose function. I am giving here a little example which may help you:
class DemoList extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
anchorEls: []
}
}
handleActionClick = (id, event) => {
let { anchorEls } = this.state;
anchorEls[id] = event.target;
this.setState({ anchorEls });
}
handleActionClose = (id, event) => {
let { anchorEls } = this.state;
anchorEls[id] = null;
this.setState({ anchorEls });
}
render() {
let { classes } = this.props;
const { data, anchorEls } = this.state;
return (
<Paper className="main">
<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">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map(row => (
<TableRow key={row.id}>
<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">
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={e => this.handleActionClick(row.id, e)}
>
<MoreVert />
</IconButton>
<Menu
id={row.id}
anchorEl={anchorEls[row.id]}
keepMounted
open={Boolean(this.state.anchorEls[row.id])}
onClose={e => this.handleActionClose(row.id, e)}
>
<MenuItem onClick={e => this.handleActionClose(row.id, e)}> View Details </MenuItem>
<MenuItem onClick={e => this.handleActionClose(row.id, e)}> Assign </MenuItem>
</Menu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Paper>
)
}
}
After hacking away I found that removing the CustomTableCell declaration and changing all the CustomTableCells to normal TableCells resolved the problem. 😫 Not sure why that would fix it given that the CustomTableCell code was pulled straight from the Demos page.