I have a simple backend code which works well with Rest API
updated code
ClientSchema.js
const mongoose = require('mongoose');
var Schema = mongoose.Schema;
const customerSchema = new Schema({
paymentReferenceCode:{
type: String,
required: true
},
paymentType:{
type: String,
required: true
},
paymentDescription:{
type: String,
required: true
},
procedureAmount:{
type: String,
required: true
},
paymentDiscount:{
type: Number,
required: true
},
AmountPaid:{
type: Number,
required: true
},
Total:{
type: Number,
required: true
},
clientdetails: {
ref: 'Client',
type: mongoose.Schema.Types.ObjectId
},
}, { timestamps: true })
module.exports = mongoose.model('Customer',customerSchema);
Client.js
const mongoose = require('mongoose');
const ClientSchema = new mongoose.Schema({
fullName:{
type:String,
required: true
},
dateofBirth:{
type:Date,
required: true
},
gender:{
type:String,
required: true
},
nationality:{
type:String,
required: true
},
address:{
type:String,
required: true
},
date:{
type:Date,
default:Date.now
}
});
module.exports = mongoose.model('Client', ClientSchema)
Router.js
router.post("/addbill",async(req,res)=>{
try {
console.log(req.params);
const clientid = await clients.findOne({ _id: req.params.id });
await new Customer({
clientdetails:clientid,
paymentReferenceCode:req.body.paymentReferenceCode,
paymentType:req.body.paymentType,
paymentDescription:req.body.paymentDescription,
procedureAmount:req.body.procedureAmount,
paymentDiscount:req.body.paymentDiscount,
AmountPaid:req.body.AmountPaid,
Total:req.body.Total
}).save(async (err, data) => {
if (err) {
console.log('err:', err);
res.status(500).json({
message: 'Something went wrong, please try again later.'
});
} else {
res.status(200).json({
message: 'Bill Created',
data,
id: data._id
});
}
});
} catch (error) {
res.status(422).json(error);
}
})
router.get('/', async (req, res) => {
try{
const data = await clients.find();
res.json(data)
}
catch(error){
res.status(500).json({message: error.message})
}
})
Frontend
Billing.js
import React, {Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css"
import axios from "axios"
import SimpleReactValidator from "simple-react-validator"
import TextField from '@mui/material/TextField';
import $ from 'jquery'
import Select,{components} from "react-select";
import Box from '@mui/material/Box';
import NativeSelect from "@mui/material/NativeSelect";
import Button from "@mui/material/Button";
class Billing extends Component {
constructor(){
super()
this.state = {
clientdetails :{},
paymentReferenceCode: "",
paymentType: "",
paymentDescription: "",
procedureAmount: "",
paymentDiscount: "",
AmountPaid: "",
Total: "",
}
this.changePaymentReferenceCode = this.changePaymentReferenceCode.bind(this)
this.changePaymentType = this.changePaymentType.bind(this)
this.changePaymentDescription = this.changePaymentDescription.bind(this)
this.changeProcedureAmount = this.changeProcedureAmount.bind(this)
this.changePaymentDiscount = this.changePaymentDiscount.bind(this)
this.changeAMountPaid = this.changeAMountPaid.bind(this)
this.changeTOtal = this.changeTOtal.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
changePaymentReferenceCode(event){
this.setState({
paymentReferenceCode:event.target.value
})
}
changeProcedureAmount(event){
this.setState({
procedureAmount:event.target.value
})
}
changePaymentType(event){
this.setState({
paymentType:event.target.value
})
}
changePaymentDescription(event){
this.setState({
paymentDescription:event.target.value
})
}
changePaymentAmount(event){
this.setState({
paymentAmount:event.target.value
})
}
changePaymentDiscount(event){
this.setState({
paymentDiscount:event.target.value
})
}
changeAMountPaid(event){
this.setState({
AmountPaid:event.target.value
})
}
changeTOtal(event){
this.setState({
Total:event.target.value
})
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
})
}
componentDidMount(){
this.loadData();
}
loadData = () => {
axios.get('http://localhost:4000/clients', {
headers: {"Access-Control-Allow-Origin": true,
'Access-Control-Allow-Credentials' : true,
'Access-Control-Allow-Methods':'GET,PUT,POST,DELETE,PATCH,OPTIONS',
crossorigin : true,
},
responseType : 'json'
})
.then((result) => {
console.log(result.data);
this.setState({
clients : result.data,
})
})
.catch((error) => {
console.log(error);
})
}
customFilter = (option, searchText) => {
if(
option.data.fullName.toLowerCase().includes(searchText.toLowerCase())
)
return true;
return false;
}
handleSubmit(event){
event.preventDefault()
const entry = {
paymentReferenceCode: this.state.paymentReferenceCode,
paymentType: this.state.paymentType,
paymentDescription: this.state.paymentDescription,
procedureAmount: this.state.procedureAmount,
paymentDiscount: this.state.paymentDiscount,
AmountPaid: this.state.AmountPaid,
Total: this.state.Total,
}
axios.post('http://localhost:4000/Bill/addbill', entry)
.then(response => {
this.setState({clientdetails: response.data})
console.log(response.data)
})
this.setState({
clientdetails:{},paymentReferenceCode: "",paymentType: "",paymentDescription: "",procedureAmount: "",
paymentDiscount: "",AmountPaid: "",Total: "",
})}
render() {
const displayNone = { display: 'none' }
return (
<div>
<div className="container">
<div className="form-div">
<p className="text-capitalize">Billing</p>
<Box component="form" onSubmit={this.handleSubmit} noValidate sx={{ mt: 1}}>
<Select
closeMenuOnSelect={true}
hideSelectedOptions={true}
options={this.state.clientdetails}
filterOption = {this.customFilter}
isClearable={true}
search={true}
components={{IndicatorSeparator: () => null,}}
placeholder={'Select Client'}
getOptionLabel={option => `${option.fullName} ${option._id}`}
onchange={this.customFilter}
></Select>
<TextField
margin="normal"
fullWidth
id="paymentReferenceCode"
label="PaymentRefernceCode"
name="paymentReferenceCode"
autoComplete="off"
value={this.state.paymentReferenceCode}
onChange={this.handleChange}
autoFocus
/>
<NativeSelect
fullWidth
onChange={this.handleChange}
value={this.state.paymentType}
inputProps={{
name: 'paymentType',
id: 'paymentType',
}}
>
<option >PaymentType</option>
<option value="Cash">Cash</option>
<option value="PayPal">PayPal</option>
<option value="MasterCard">MasterCard</option>
</NativeSelect>
<TextField
margin="normal"
fullWidth
InputLabelProps={{style : {color : 'black'} }}
id="paymentDescription"
label="Payment Description"
name="paymentDescription"
autoComplete="paymentDescription"
onChange={this.handleChange}
value={this.state.paymentDescription}
autoFocus
/>
<TextField
margin="normal"
fullWidth
id="AmountPaid"
label="Amount Paid"
name="AmountPaid"
autoComplete="AmountPaid"
onChange={this.handleChange}
value={this.state.AmountPaid}
autoFocus
/><TextField
margin="normal"
fullWidth
id="paymentDiscount"
label="Payment Discount"
name="paymentDiscount"
autoComplete="paymentDiscount"
onChange={this.handleChange}
value={this.state.paymentDiscount}
autoFocus
/>
<TextField
margin="normal"
fullWidth
id="procedureAmount"
label="Procedure Amount"
name="procedureAmount"
autoComplete="procedureAmount"
onChange={this.handleChange}
value={this.state.procedureAmount}
autoFocus
/>
<TextField
margin="normal"
fullWidth
id="Total"
label="Total Bill"
name="Total"
autoComplete="Total"
onChange={this.handleChange}
value={this.state.Total}
autoFocus
/>
<div id='loginSuccess' className="alert alert-success" style={displayNone} role="alert">
<strong>Success! </strong>Client Bill Entered Successful.
</div>
<Button
type="submit"
fullWidth
sx={{ mt: 3, mb: 2}}
>
<span className='btn btn-warning btn-block form-control form-group'>Submit</span>
</Button>
</Box>
</div>
</div>
</div>
);
}
}
export default Billing;
I tried to use axios.post and submit the form. However, am not able to retrieve clientdetails data to the frontend in particular the select part of the form, it returns null. But the other entries go through to the backend. This is what am getting in the console. data: AmountPaid: 100 Total: 100 createdAt: "2022-11-25T22:31:57.306Z" clientdetails: null paymentDescription: "accomodation" paymentDiscount: 100 paymentReferenceCode: "2345" paymentType: "Cash" procedureAmount: "3" updatedAt: "2022-11-25T22:31:57.306Z" __v: 0 _id: "6381425db019f3f9a48047ae" [[Prototype]]: Objectid: "6381425db019f3f9a48047ae"
I would like to retrieve the clientdetails data to the frontend select section, select an option and be able to submit all the data.Thank you
CodePudding user response:
You have a few problems, both on front-end and back-end.
Try to change your component like this:
class Billing extends Component {
constructor() {
super();
this.state = {
clients: [],
clientdetails: '',
paymentReferenceCode: '',
paymentType: '',
paymentDescription: '',
procedureAmount: '',
paymentDiscount: '',
AmountPaid: '',
Total: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleChangeClientDetails = this.handleChangeClientDetails.bind(this);
}
componentDidMount() {
this.loadData();
}
handleChange(event) {
this.setState({
...this.state,
[event.target.name]: event.target.value,
});
}
handleChangeClientDetails(newValue) {
this.setState({
...this.state,
clientdetails: newValue._id
})
}
loadData = () => {
axios
.get('http://localhost:4000/clients', {
headers: {
'Access-Control-Allow-Origin': true,
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
crossorigin: true,
},
responseType: 'json',
})
.then((result) => {
console.log(result.data);
this.setState({
...this.state,
clients: result.data,
});
})
.catch((error) => {
console.log(error);
});
};
customFilter = (option, searchText) => {
if (option.data.fullName.toLowerCase().includes(searchText.toLowerCase()))
return true;
return false;
};
handleSubmit(event) {
event.preventDefault();
const entry = {
paymentReferenceCode: this.state.paymentReferenceCode,
paymentType: this.state.paymentType,
paymentDescription: this.state.paymentDescription,
procedureAmount: this.state.procedureAmount,
paymentDiscount: this.state.paymentDiscount,
AmountPaid: this.state.AmountPaid,
Total: this.state.Total,
};
if (!this.state.clientdetails) {
console.log('Select client');
return;
}
axios.post(`http://localhost:4000/Bill/addbill/${this.state.clientdetails}`, entry).then((response) => {
console.log(response.data);
});
this.setState({
clientdetails: '',
paymentReferenceCode: '',
paymentType: '',
paymentDescription: '',
procedureAmount: '',
paymentDiscount: '',
AmountPaid: '',
Total: '',
});
}
render() {
const displayNone = { display: 'none' };
return (
<div>
<div className='container'>
<div className='form-div'>
<p className='text-capitalize'>Billing</p>
<Box
component='form'
onSubmit={this.handleSubmit}
noValidate
sx={{ mt: 1 }}
>
<Select
closeMenuOnSelect={true}
hideSelectedOptions={true}
options={this.state.clients}
filterOption={this.customFilter}
isClearable={true}
search={true}
components={{ IndicatorSeparator: () => null }}
placeholder={'Select Client'}
getOptionLabel={(option) => `${option.fullName} ${option._id}`}
onChange={this.handleChangeClientDetails} />
...
</div>
</div>
</div>
);
}
}
export default Billing;
And add the parameter to the POST route:
router.post("/addbill/:id", ... );
CodePudding user response:
At your router you're using req.params.id
but it's never declared, since your clientdetails
uses the clientid
, it'll be null
. Easy to fix.
router.post("/addbill",async(req,res)=>{
try {
console.log(req.params);
const clientid = await clients.findOne({ _id: req.params.id });
should be
// /-- req.params.id
router.post("/addbill/:id",async(req,res)=>{
try {
console.log(req.params);
const clientid = await clients.findOne({ _id: req.params.id });
just remember that now your route will need the /clientid
at the end.