Upload Image and indicating response in a table with axios and JavaScript - javascript

I want to upload 10 images together and send a axios request to backend to do some calculations with those 10 files , after calculations there will be response as {imagename: true} or {imagename: false} receiving the response from backend I want to list those 10 images on frontend with a indication that the calculation is true or false.
This is what I tried but I'm stuck after getting response and unable to show the true or false status.
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { Grid, Row, Col } from 'react-flexbox-grid';
import axios, { post } from 'axios';
import compose from 'recompose/compose';
import { withStyles } from '#material-ui/core/styles';
import Styles from '../styles';
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
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 { Scrollbars } from 'react-custom-scrollbars';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
class ImageUploader extends React.Component {
constructor(props) {
super(props);
this.state ={
file:null,
user_name:this.props.username,
checkboxcolor:false,
detailsResoponse:[],
responseList:[],
imageList:[],
uploadResponse:'priya',
a:[],
loaded: 0
}
this.onFormSubmit = this.onFormSubmit.bind(this)
this.handelFile = this.handelFile.bind(this)
this.fileUpload = this.fileUpload.bind(this)
}
onFormSubmit(){
this.setState({
responseList:[]
})
var files=this.state.file
for(var i in files){
this.fileUpload(files[i])
.then((response)=>{
// console.log(response.data);
})
}
}
handelFile(e) {
if(e.target.files.length == 10) {
var self=this
self.setState({
imgList:[],
file:e.target.files,
})
for(var i in e.target.files){
if(i != 'length' && i != 'item'){
if(e.target.files[i].type.split('/')[0] == 'image'){
self.state.imageList.push(e.target.files[i])
}
}
}
}
else{
alert('Please upload 10 images')
}
}
urlBlob(id,file){
var reader = new FileReader();
reader.onload = function (e) {
var image=document.getElementById(id)
image.src=e.target.result
}
reader.readAsDataURL(file);
}
fileUpload(file){
const url = 'http://abc';
const formData = new FormData();
formData.append('image',file)
const config = {
headers: {
'content-type': 'multipart/form-data',
'Access-Control-Allow-Origin':'*'
}
}
return axios.post(url,formData,config)
.then(res => {
var jsondata=JSON.stringify(res.data)
JSON.parse(jsondata, (key, value) => {
// if (value == true) {
// this.state.a.push(key)
var arrayList= this.state.responseList
arrayList.push(res.data)
this.setState({
responseList:arrayList,
// checkboxcolor:true
})
// }
});
}
)
.catch(function (error) {
alert(error)
});
}
render(){
const { classes } = this.props;
console.log(this.state.a)
console.log(this.state.imageList,"yep")
// console.log(this.state.responseList,"responseList")
return (
<div>
<Grid>
<Row>
<Col sm={12} md={12} lg={12}>
<AppBar position="static" color="inherit" className={classes.app}>
<Toolbar>
<Typography variant="title" color="inherit">
Upload Image
</Typography>
</Toolbar>
</AppBar>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<Paper elevation={3} style={{padding:20,height:25,marginBottom:20}}>
<input
id="fileItem"
type="file" onChange={this.handelFile}
multiple
/>
<Button color="primary" onClick={this.onFormSubmit}> Upload</Button>
</Paper>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<Table style={{width:'80%',position:'relative',left:'8%',border:'2px solid lightgrey',marginTop:'3%'}}>
<TableHead>
<TableRow >
<TableCell className={classes.phPadding}> Checkbox </TableCell>
<TableCell className={classes.phPadding}> Image </TableCell>
<TableCell className={classes.phPadding}> Name </TableCell>
<TableCell className={classes.phPadding}> Username</TableCell>
<TableCell style={{width:'10%'}}></TableCell>
</TableRow>
</TableHead>
</Table>
<Scrollbars style={{height: 328}}>
{this.state.imageList.map((item,key)=> (
<Table style={{width:'80%',position:'relative',left:'8%',border:'2px solid lightgrey',borderTop:'0px'}}>
<TableBody>
<TableRow key={key}>
<TableCell className={classes.phPadding}>
{this.state.checkboxcolor ?
<FormControlLabel
control={
<Checkbox
checked={this.state.checkboxcolor}
/>
}
/>
:
null
}
</TableCell>
<TableCell className={classes.phPadding}>
<img id={"image"+key} src={this.urlBlob("image"+key,item)} height="90" width="90" />
</TableCell>
<TableCell className={classes.phPadding}>{item.name}</TableCell>
<TableCell className={classes.phPadding}>{/* {this.state.user_name} */}user_name</TableCell>
<TableCell>
</TableCell>
</TableRow>
</TableBody>
</Table>
))}
</Scrollbars>
</Col>
</Row>
</Grid>
</div>
)
}
}
export default compose(withStyles(Styles))(ImageUploader);
console.log(res.data) response

EDIT: To fix below issue you need to add event.persist() under handleFile function.
Uncaught TypeError: Cannot read property 'files' of null
I updated all the code using ES6.
I noticed some bad practices in your code and corrected them
The way you push elements to state array is not right instead use
previous state to play with array in react state.
Stop using var
instead use let and const
Stop assigning this to self instead use arrow
functions so that you no need to do manual binding and no need to depend on local variable i.e., self
I have corrected your code. This code will show the image responses as you needed
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { Grid, Row, Col } from 'react-flexbox-grid';
import axios, { post } from 'axios';
import compose from 'recompose/compose';
import { withStyles } from '#material-ui/core/styles';
import Styles from '../styles';
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
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 { Scrollbars } from 'react-custom-scrollbars';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
class ImageUploader extends React.Component {
constructor(props) {
super(props);
this.state ={
file:null,
user_name:this.props.username,
detailsResoponse:[],
responseList:[],
imageList:[],
uploadResponse:'priya',
a:[],
loaded: 0
}
}
onFormSubmit = () => {
this.setState({
responseList:[]
})
let files = this.state.file;
for(var i in files){
this.fileUpload(files[i]);
}
}
handelFile = e => {
e.persist();
if(e.target.files.length == 10) {
this.setState({
imgList:[],
file:e.target.files,
})
for(let i=0; i < e.target.files.length; i++){
if(e.target.files[i].type.split('/')[0] == 'image'){
this.setState(prevState => ({
imageList: [...prevState.imageList, e.target.files[i]]
}));
}
}
}
else{
alert('Please upload 10 images')
}
}
urlBlob = (id,file) => {
let reader = new FileReader();
reader.onload = e => {
let image = document.getElementById(id);
image.src = e.target.result;
}
reader.readAsDataURL(file);
}
fileUpload = file => {
const url = 'http://172.16.92.21:9999';
const formData = new FormData();
formData.append('image',file)
formData.append('uname','150180')
const config = {
headers: {
'content-type': 'multipart/form-data',
'Access-Control-Allow-Origin':'*'
}
}
axios.post(url, formData, config)
.then(res => {
this.setState(prevState => ({
responseList: [...prevState.responseList, res.data]
}));
});
.catch(error => {
alert(error)
});
}
render(){
const { classes } = this.props;
const { responseList } = this.props;
console.log(this.state.a)
console.log(this.state.imageList,"yep")
// console.log(this.state.responseList,"responseList")
const imagesResponse = responseList && responseList.map((image, index) => {
return <li className="list-group-item">{image}</li>
});
return (
<div>
<Grid>
<Row>
<Col>
<ul className="list-group">
{imagesResponse}
</ul>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<AppBar position="static" color="inherit" className={classes.app}>
<Toolbar>
<Typography variant="title" color="inherit">
Upload Image For Surveillance
</Typography>
</Toolbar>
</AppBar>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<Paper elevation={3} style={{padding:20,height:25,marginBottom:20}}>
<input
id="fileItem"
type="file" onChange={this.handelFile}
multiple
/>
<Button color="primary" onClick={this.onFormSubmit}> Upload</Button>
</Paper>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<Table style={{width:'80%',position:'relative',left:'8%',border:'2px solid lightgrey',marginTop:'3%'}}>
<TableHead>
<TableRow >
<TableCell className={classes.phPadding}> Checkbox </TableCell>
<TableCell className={classes.phPadding}> Image </TableCell>
<TableCell className={classes.phPadding}> Name </TableCell>
<TableCell className={classes.phPadding}> Username</TableCell>
<TableCell style={{width:'10%'}}></TableCell>
</TableRow>
</TableHead>
</Table>
<Scrollbars style={{height: 328}}>
{this.state.imageList && this.state.imageList.map((item,key)=> (
<Table style={{width:'80%',position:'relative',left:'8%',border:'2px solid lightgrey',borderTop:'0px'}}>
<TableBody>
<TableRow key={key}>
<TableCell className={classes.phPadding}>
{responseList ? responseList.map((image, index) => {
return (<FormControlLabel key={index}
control={
<Checkbox
checked={item.name == image.name && image.flag ? true : false }
/>
}
/>)
}): <FormControlLabel
control={
<Checkbox
checked={false}
/>
}
/>
}
</TableCell>
<TableCell className={classes.phPadding}>
<img id={"image"+key} src={this.urlBlob("image"+key,item)} height="90" width="90" />
</TableCell>
<TableCell className={classes.phPadding}>{item.name}</TableCell>
<TableCell className={classes.phPadding}>{/* {this.state.user_name} */}user_name</TableCell>
<TableCell>
</TableCell>
</TableRow>
</TableBody>
</Table>
))}
</Scrollbars>
</Col>
</Row>
</Grid>
</div>
)
}
}
export default compose(withStyles(Styles))(ImageUploader);
Please test from your side and let me know

Related

React-Bootstrap - How to create rows of 4 cards each?

how do I set the number of cards per row? I need 4 cards to be in one row of mine.
I have been trying for hours.. can't figure out what i do wrong..
I was getting all sorts of errors when i played around with this.
like this one :
here
my package.json file:
thanks!
:)
import { React } from 'react';
import { Card, Button, CardGroup } from 'react-bootstrap';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
const MyDishes = props => {
const { dishes } = props;
return (
<CardGroup style={{display: 'flex', flexDirection: 'row'}}>
{dishes.length > 0 &&
dishes.map(d => (
<Row xs={1} md={2} className="g-4">
{Array.from({ length: 4 }).map((_, idx) => (
<Col>
<Card key={d.id} style={{ width: '100em' }} style={{flex: 1}}>
<Card.Img variant="top" src={d.attributes.picture} />
<Card.Body>
<Card.Title>{d.attributes.name}</Card.Title>
<Card.Text>Possibly some text here</Card.Text>
<Link to={`/dishes/${d.id}`}>
<Button variant="primary">Full Recipe</Button>
</Link>
</Card.Body>
</Card>
</Col>
))}
</Row>
</CardGroup>
);
};
const mapStateToProps = state => {
return {
dishes: state.myDishes
};
};
export default connect(mapStateToProps)(MyDishes);
i get this error now :
To use bootstrap layout system, must use <Container> to wrap <Row> and <Col>
You don't need custom styles to achieve that you want.
import { React } from 'react';
import { Container, Card, Button, CardGroup } from 'react-bootstrap';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
const MyDishes = props => {
const { dishes } = props;
return (
<Container>
{dishes.length > 0 &&
dishes.map(d => (
<Row xs={1} md={2} className="g-4">
{Array.from({ length: 4 }).map((_, idx) => (
<Col>
<Card key={d.id}>
<Card.Img variant="top" src={d.attributes.picture} />
<Card.Body>
<Card.Title>{d.attributes.name}</Card.Title>
<Card.Text>Possibly some text here</Card.Text>
<Link to={`/dishes/${d.id}`}>
<Button variant="primary">Full Recipe</Button>
</Link>
</Card.Body>
</Card>
</Col>
))}
</Row>
))}
</Container>
);
};
const mapStateToProps = state => {
return {
dishes: state.myDishes
};
};
export default connect(mapStateToProps)(MyDishes);

Material UI React Table Pagination

I want to inquire about Table Pagination in React UI Material.
Here I am trying to implement all the data from the API.
Some of the data that I have successfully implemented is from the API to the Material UI Table.
Bring up data based on request Rows Per Page
Bring up the data comes from the API that I have.
However when I try to implement Pagination, the Data doesn't appear properly from the API.
Example:
When the Component is rendered for the first time, it will display 5 data from the API. But when I tried to move to the next page, only the response from the API was successful, but not the data that appeared.
Here I divide it into several files:
Index Component
Index Container
File Index Component.
import React, { useState } from 'react'
// * Material UI
import {
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Grid,
TableContainer,
Table,
TableHead,
TableRow,
TableCell,
TableBody,
TablePagination,
CssBaseline
} from '#material-ui/core'
import {
Add
} from '#material-ui/icons'
// * Core Component
import Layout from '#containers/v2/core/Layout'
import InputCategoryComponent from './Input'
import CardCore from '../../core/shared/Card'
import Controls from '../../core/shared/controls'
import { isEmpty } from '#client/libs/utils'
const columns = [
{ id: 'name', label: 'Name', minWidth: 500 },
{ id: 'Actions', label: 'Action', minWidth: 100 }
]
export default function CategoryComponent ({
page = 0,
rowsPerPage = 5,
rowsPerPageOptions = [5, 10, 15, 20, 25],
startDate = '',
endDate = '',
sortBy = 'created_at',
sortDirection = 'desc',
search = '',
totalCount = 0,
items = [],
fetch = () => {},
createPaymentCategory = () => {},
readPaymentCategory = () => {},
updatePaymentCategory = () => {},
deletePaymentCategory = () => {},
handleRowPerPage = () => {},
handlePagination = () => {}
}) {
const [isTable, setTable] = useState(true)
const [alert, setAlert] = useState(false)
const [hashed, setHashed] = useState('')
const [initialModel, setModel] = useState([])
const emptyRows = rowsPerPage - Math.min(rowsPerPage, totalCount - page * rowsPerPage)
const handleTable = (params) => {
setTable(params)
}
const handleAlertOpen = (payload) => {
setHashed(payload)
setAlert(true)
}
const handleChange = (payload) => {
if (isEmpty(payload)) {
setModel({
name: ''
})
setTable(false)
} else {
readPaymentCategory(payload).then((res) => {
setModel(res.data)
setTable(false)
})
}
}
const handleAlertClose = () => {
setAlert(false)
}
const onFinish = () => {
fetch(
page,
rowsPerPage,
startDate,
endDate,
sortBy,
sortDirection,
search
)
setTable(true)
setModel({
name: ''
})
}
const onDelete = () => {
deletePaymentCategory(hashed).then(() => {
setHashed('')
setAlert(false)
fetch(
page,
rowsPerPage,
startDate,
endDate,
sortBy,
sortDirection,
search
)
})
}
const onRowChange = (e) => {
handleRowPerPage(e.target.value)
fetch(
0,
e.target.value,
startDate,
endDate,
sortBy,
sortDirection,
search
)
}
const onPageChange = (event, newPage) => {
handlePagination(newPage)
fetch(
newPage,
rowsPerPage,
startDate,
endDate,
sortBy,
sortDirection,
search
)
}
return (
<Layout>
<CssBaseline />
{isTable && (
<Grid container>
<Grid item sm={12} md={12} lg={12}>
<CardCore
variant='info'
title='Payment Category'
>
<Grid container>
<Grid item sm={12} md={12} lg={!2}>
<Controls.Button
text='Create'
color='primary'
startIcon={<Add />}
onClick={() => handleChange()}
/>
</Grid>
</Grid>
<Grid container>
<Grid item sm={12} md={12} lg={!2}>
<TableContainer>
<Table stickyHeader aria-label='sticky table'>
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{ minWidth: column.minWidth }}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{items.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => {
return (
<TableRow hover role='checkbox' tabIndex={-1} key={row.name}>
<TableCell>{row.name}</TableCell>
<TableCell align='left'>
<div>
<Controls.Button
text='Edit'
color='inherit'
onClick={() => handleChange(row.hashed_id)}
/>
<Controls.Button
text='Delete'
color='secondary'
onClick={() => handleAlertOpen(row.hashed_id)}
/>
</div>
</TableCell>
</TableRow>
)
})}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={rowsPerPageOptions}
component='div'
count={totalCount}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={onPageChange}
onChangeRowsPerPage={onRowChange}
/>
</Grid>
</Grid>
</CardCore>
</Grid>
</Grid>
)}
<Dialog
open={alert}
onClose={handleAlertClose}
fullWidth
maxWidth='sm'
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle id='alert-dialog-title'>Are you sure want to delete this?</DialogTitle>
<DialogContent>
<DialogContentText id='alert-dialog-description'>
You wont be able to revert this!
</DialogContentText>
</DialogContent>
<DialogActions>
<Controls.Button
text='Disagree'
color='secondary'
onClick={handleAlertClose}
/>
<Controls.Button
text='Agree'
color='primary'
autoFocus
onClick={onDelete}
/>
</DialogActions>
</Dialog>
{!isTable && (
<InputCategoryComponent
controlTable={handleTable}
initialValues={initialModel}
callBack={onFinish}
createData={createPaymentCategory}
updateData={updatePaymentCategory}
/>
)}
</Layout>
)
}
File Index Container.
import React, { Component } from 'react'
import { mapStateToProps, mapActions } from '#client/store'
import { connect } from 'react-redux'
// * Category Component
import CategoryComponent from '#components/v2/Payment/Category/Index'
class PaymentCategoryContainer extends Component {
constructor (props) {
super(props)
this.state = {
page: 0,
rowsPerPage: 5,
rowsPerPageOptions: [5, 10, 15, 20, 25],
startDate: '',
endDate: '',
sortBy: 'created_at',
sortDirection: 'desc',
search: '',
totalCount: 0,
items: []
}
this.fetch = this.fetch.bind(this)
this.handleRow = this.handleRow.bind(this)
this.handlePage = this.handlePage.bind(this)
}
fetch (page, rowsPerPage, startDate, endDate, sortBy, sortDirection, search) {
this.props.fetchPaymentCategory(
page,
rowsPerPage,
startDate,
endDate,
sortBy,
sortDirection,
search
).then((res) => {
if (res?.status === 200) {
this.setState({
items: res?.data.items,
totalCount: res?.data.totalItems
})
} else {
console.log('error')
}
})
}
handleRow (rowsPerPage) {
this.setState({
rowsPerPage: rowsPerPage
})
const { page, startDate, endDate, sortBy, sortDirection, search } = this.state
this.fetch(
page,
rowsPerPage,
startDate,
endDate,
sortBy,
sortDirection,
search
)
}
handlePage (numberOfPage) {
this.setState({
page: numberOfPage
})
const { rowsPerPage, startDate, endDate, sortBy, sortDirection, search } = this.state
this.fetch(
numberOfPage,
rowsPerPage,
startDate,
endDate,
sortBy,
sortDirection,
search
)
}
componentDidMount () {
this.fetch(
this.state.page,
this.state.rowsPerPage,
'',
'',
this.state.sortBy,
this.state.sortDirection,
''
)
}
render () {
return (
<CategoryComponent
{...this.props}
{...this.state}
fetch={this.fetch}
handleRowPerPage={this.handleRow}
handlePagination={this.handlePage}
/>
)
}
}
export default connect(
mapStateToProps('paymentCategory'),
mapActions(
'fetchPaymentCategory',
'changeRowPerPage',
'changePagination',
'createPaymentCategory',
'readPaymentCategory',
'updatePaymentCategory',
'deletePaymentCategory'
)
)(PaymentCategoryContainer)
Response data from API:
items: [] => array. totalItems: 0 => int
Are there any mistakes that I missed? or is there a step that i missed ?.
I am following the examples in this section ui material.
Thank you.
I have found where the mistake I made. I shouldn't need to slice() the data. Because I have a params page query so I just need to map() something like this.
return (
<Layout>
<CssBaseline />
{isTable && (
<Grid container>
<Grid item sm={12} md={12} lg={12}>
<CardCore
variant='info'
title='Payment Category'
>
<Grid container>
<Grid item sm={12} md={12} lg={!2}>
<Controls.Button
text='Create'
color='primary'
startIcon={<Add />}
onClick={() => handleChange()}
/>
</Grid>
</Grid>
<Grid container>
<Grid item sm={12} md={12} lg={!2}>
<TableContainer>
<Table stickyHeader aria-label='sticky table'>
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{ minWidth: column.minWidth }}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{items.map((row) => {
return (
<TableRow hover role='checkbox' tabIndex={-1} key={row.name}>
<TableCell>{row.name}</TableCell>
<TableCell align='left'>
<div>
<Controls.Button
text='Edit'
color='inherit'
onClick={() => handleChange(row.hashed_id)}
/>
<Controls.Button
text='Delete'
color='secondary'
onClick={() => handleAlertOpen(row.hashed_id)}
/>
</div>
</TableCell>
</TableRow>
)
})}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={rowsPerPageOptions}
component='div'
count={totalCount}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={onPageChange}
onChangeRowsPerPage={onRowChange}
/>
</Grid>
</Grid>
</CardCore>
</Grid>
</Grid>
)}
<Dialog
open={alert}
onClose={handleAlertClose}
fullWidth
maxWidth='sm'
aria-labelledby='alert-dialog-title'
aria-describedby='alert-dialog-description'
>
<DialogTitle id='alert-dialog-title'>Are you sure want to delete this?</DialogTitle>
<DialogContent>
<DialogContentText id='alert-dialog-description'>
You wont be able to revert this!
</DialogContentText>
</DialogContent>
<DialogActions>
<Controls.Button
text='Disagree'
color='secondary'
onClick={handleAlertClose}
/>
<Controls.Button
text='Agree'
color='primary'
autoFocus
onClick={onDelete}
/>
</DialogActions>
</Dialog>
{!isTable && (
<InputCategoryComponent
controlTable={handleTable}
initialValues={initialModel}
callBack={onFinish}
createData={createPaymentCategory}
updateData={updatePaymentCategory}
/>
)}
</Layout>
)
}
Hope this helps others.

Can't read property 'map' of undefined in React app

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.

how to render components based on the value of props?

I have a parent component Dashboard.js. Here I have three values of state namely yesterday, lastWeek, lastMonth and I'm passing this value to my child component. Now I want to render my data depending on my child component. The problem is I'm using componentDidMount() lifecycle method which is rendering the child component only once. How do I render the data based on the props passed to the child component? The parent component is a react hook and the child component called DataFetchDetails is a class based component. Attaching their respective codes
Parent Component :- Dashboard.js
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import { Tabs, Tab, Grid } from '#material-ui/core';
import AppBar from '#material-ui/core/AppBar';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
import PropTypes from 'prop-types';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select'
import {
TotalUsers,
LoggedInUsers,
TimePicker,
UnregisteredUsers
} from './components';
import DataFetchDetails from './components/DataFetchDetails';
const useStyles = makeStyles(theme => ({
root: {
paddingTop: theme.spacing(4),
padding: theme.spacing(4)
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<Typography
component="div"
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
<Box p={3}>{children}</Box>
</Typography>
);
}
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
const Dashboard = () => {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
const [period, setPeriod] = React.useState("yesterday");
const handleChange1 = event => {
setPeriod(event.target.value);
};
return (
<div className={classes.root}>
<Select
labelId="demo-simple-select-label"
id="demo-sample-select"
value={time}
onChange={handleChange1}
>
<MenuItem value={"yesterday"}>Previous day</MenuItem>
<MenuItem value={"lastWeek"}>Last Week</MenuItem>
<MenuItem value={"lastMonth"}>Last Month</MenuItem>
</Select>
<div className={classes.root}>
<AppBar position="static">
<Tabs value={value} onChange={handleChange} aria-label="simple tabs example">
<Tab label="CONSENT DETAILS" {...a11yProps(0)} />
<Tab label="ACCOUNT DETAILS" {...a11yProps(1)} />
<Tab label="DATA FETCH DETAILS" {...a11yProps(2)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
</TabPanel>
<TabPanel value={value} index={1}>
</TabPanel>
<TabPanel value={value} index={2}>
<DataFetchDetails period={period} handlePeriodChange1={handleChange1} />
</TabPanel>
</div>
</div>
);
};
export default Dashboard;
Child component DataFetchDetails.js :-
import React from 'react';
import {
Card,
CardHeader,
Button,
Divider,
CardContent,
TextField,
CardActions,
FormControl,
InputLabel,
Select,
MenuItem
} from '#material-ui/core';
import Paper from '#material-ui/core/Paper';
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 axios from 'axios';
import 'izitoast/dist/css/iziToast.min.css'; // loading css
import iziToast from 'izitoast/dist/js/iziToast.min.js'; // you have access to iziToast now
import 'izitoast/dist/css/iziToast.min.css';
const url = 'MY_ENDPOINT_URL';
export default class DataFetchDetails extends React.Component {
constructor(props) {
super(props);
this.state = {
items : [],
isLoaded : true,
renderJsx: false,
}
}
componentDidMount() {
this.setState({period: this.props.period});
const periodStatus = {
period : this.props.period
};
{console.log("Props period = ",this.props.period)}
axios.post(url, periodStatus)
.then((response) => {
this.setState({period : this.props.period})
this.setState({items : [response.data]});
.catch((error) => {
console.log("Error");
});
}
render() {
let {isLoaded, items, renderJsx } = this.state;
if(!isLoaded) {
return <div>Loading</div>
}
else {
return (
<div>
<div>
<Card className="Lock-user"
>
<form >
<CardHeader
title=""
/>
<Divider></Divider>
<CardContent id="form-input" className=""
>
</CardContent>
<Divider></Divider>
</form>
</Card>
</div>
<div>
<Card>
<Paper>
<Table>
<TableHead>
<TableRow>
<TableCell> success </TableCell>
<TableCell align="right">failure</TableCell>
<TableCell align="right">inProgress</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ items.map(item => (
<TableRow key={item.success}>
<TableCell component="th" scope="row">
{item.success}
</TableCell>
<TableCell align="right">{item.failure}</TableCell>
<TableCell align="right">{item.inProgress}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{console.log("Props period render = ",this.props.period)}
</Paper>
</Card>
</div>
</div>
);
}
}
}
the backend and the api works fine. I want to re render my child component based on the value of the period. How do I solve this problem?
You you compare the props i.e prevProps and current props(this.props) object inside
ComponentDidUpdate
lifecycle method to re-render the child component based on props.
As ComponentWillReceiveProps is deprecated now.
https://egghead.io/lessons/react-refactor-componentwillreceiveprops-to-getderivedstatefromprops-in-react-16-3
Go through the react lifecycle docs or https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/postrender_with_componentdidupdate.html.
Use componentWillRecieveProps in child component.
componentWillRecieveProps(props) {
// props => new props passed
// this.props => current props
}
I hope that helps.

How to split a component and keep props working

I have this section of a component I want to move apart and keep its props working. The way it is below works when it is within the parent component.
<TableExpandedRow key={rowExpanded.id}>
<TableCell colSpan={headers.length + 1}>
<div>
{rowExpanded &&
rowExpanded.billingItems &&
rowExpanded.billingItems.map(
item =>
rowExpanded.id ===
item.cancellationRequestId && (
<div key={item.id}>
<p>
cancellationRequestId:{' '}
{item.cancellationRequestId}
</p>
</div>
),
)}
</div>
</TableCell>
</TableExpandedRow>
So I want to make a component like this
import React from 'react';
import PropTypes from 'prop-types';
import { DataTable } from 'carbon-components-react';
const { TableExpandedRow, TableCell } = DataTable;
const TableExpandedRowComp = ({ rowExpanded, rowExpandedId, itemId, headersLength, keyId }) => (
<TableExpandedRow key={keyId}>
<TableCell colSpan={headersLength}>
<div>
{rowExpanded &&
rowExpanded.billingItems &&
rowExpanded.billingItems.map(
item =>
rowExpanded.id === item.cancellationRequestId && (
<div key={item.id}>
<p>cancellationRequestId: {item.cancellationRequestId}</p>
</div>
),
)}
</div>
</TableCell>
</TableExpandedRow>
);
TableExpandedRow.propTypes = {
rowExpanded: PropTypes.object,
headersLength: PropTypes.array,
keyId: PropTypes.object,
};
export default TableExpandedRowComp;
And then import it at where it was before like: <TableExpandedRow {...props} />
The whole component looks like this, it is a datable:
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { DataTable } from 'carbon-components-react';
import TableHeaders from '../TableHeaders';
import TablePagination from '../TablePagination';
import TableToolbarComp from '../TableToolbarComp';
const {
TableContainer,
TableRow,
TableExpandHeader,
TableExpandRow,
TableExpandedRow,
Table,
TableHead,
TableHeader,
TableBody,
TableCell,
} = DataTable;
function CancellationsTable({ t, tableRows }) {
return (
<div>
<DataTable
rows={tableRows}
headers={TableHeaders(t)}
render={({ rows, headers, getHeaderProps, getRowProps }) => (
<TableContainer>
<TableToolbarComp />
<Table zebra={false} short>
<TableHead>
<TableRow>
<TableExpandHeader />
{headers.map(header => (
<TableHeader {...getHeaderProps({ header })}>
{header.header}
</TableHeader>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (
<React.Fragment key={row.id}>
<TableExpandRow {...getRowProps({ row })}>
{row.cells.map(cell => (
<TableCell key={cell.id}>{cell.value}</TableCell>
))}
</TableExpandRow>
{row.isExpanded &&
tableRows.map(
rowExpanded =>
row.id === rowExpanded.id && (
// THIS IS THE COMPONENT I WANT TO MOVE APART
<TableExpandedRow key={rowExpanded.id}>
<TableCell colSpan={headers.length + 1}>
<div>
{rowExpanded &&
rowExpanded.billingItems &&
rowExpanded.billingItems.map(
item =>
rowExpanded.id ===
item.cancellationRequestId && (
<div key={item.id}>
<p>
cancellationRequestId:{' '}
{item.cancellationRequestId}
</p>
</div>
),
)}
</div>
</TableCell>
</TableExpandedRow>
),
)}
</React.Fragment>
))}
</TableBody>
</Table>
</TableContainer>
)}
/>
</div>
);
}
CancellationsTable.propTypes = {
t: PropTypes.func.isRequired,
tableRows: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
};
export default translate()(CancellationsTable);
Any help?
I think that you're saying that you want to consume the props, and pass the props to the child untouched?
To do this, you need to destructure in a separate statement.
<DataTable
rows={tableRows}
headers={TableHeaders(t)}
render={props => {
const { rows, headers, getHeaderProps, getRowProps } = props;
// omitted
return <TableExpandedRow {...props} />
}}

Categories

Resources