Updating products with fetch and input value fields - node.js

I have spent whole day trying to figure this out. I want to update course recipes from my database (MongoDB) using my REST API call (Node.js with Express) by sumbiting input fields with new values of the recipe. I tried to show previous values by using input value="", but as I learned this makes it to be static. I tried to change it into dynamic accordingly to what I found online however none of tutorials I found would show what I am looking for. As you can see in code below I am trying to PUT new data that was previously set using setState(). Sadly I do not know how can I do it like this. Could you tell me if it is even possible and if so where can I learn to do it?
Here is code from React:
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import Alert from "../elements/Alert";
import Axios from "axios";
export default function UpdateCourse() {
const id = window.location.href.split("?")[1];
const [dishName, setdishName] = useState("");
const [category, setcategory] = useState("");
const [author, setauthor] = useState("");
const [ingredients, setingredients] = useState([]);
const [cookingTime, setcookingTime] = useState("");
const [sourceUrl, setsourceUrl] = useState("");
const [imageUrl, setimageUrl] = useState("");
const [isPublished, setisPublished] = useState("true");
const [price, setprice] = useState("");
const [tags, settags] = useState([]);
const [alert, setAlert] = useState("");
const history = useHistory();
const url = `http://localhost:1234/api/courses/find/${id}`;
const old = async () => {
const result = await Axios.get(url);
setdishName(result.data.dishName);
setcategory(result.data.category);
setauthor(result.data.author);
setingredients(result.data.ingredients);
setcookingTime(result.data.cookingTime);
setsourceUrl(result.data.sourceUrl);
setimageUrl(result.data.imageUrl);
setisPublished(result.data.isPublished);
setprice(result.data.price);
settags(result.data.tags);
};
old();
console.log(old);
async function update() {
let item = {
dishName,
category,
author,
ingredients,
cookingTime,
sourceUrl,
imageUrl,
isPublished,
price,
tags,
};
console.log(item);
console.log(JSON.stringify(item));
const result = await fetch(`http://localhost:1234/api/courses/${id}`, {
method: "PUT",
body: JSON.stringify(item),
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
console.log(result);
if (result.status !== 200) {
return setAlert(result.status + " " + result.statusText);
}
history.push("/");
window.location.reload();
}
return (
<div className="col-sm-6" style={{ textAlign: "center" }}>
<h1 className="bigBar">Update recipe</h1>
<div style={{ marginLeft: "3.5rem" }}>
{alert !== "" && <Alert alert={alert}></Alert>}
</div>
<input
autoFocus="autofocus"
required="required"
type="text"
className="form-control"
placeholder={dishName}
value={dishName}
onChange={e => setdishName(e.target.value)}
/>
<br />
<input
required="required"
type="text"
className="form-control"
placeholder={category}
value={category}
onChange={e => setcategory(e.target.value)}
/>
<br />
<input
type="text"
onChange={e => setauthor(e.target.value)}
className="form-control"
required="required"
placeholder={author}
value={author}
/>
<br />
<input
type="text"
onChange={e => setingredients(e.target.value)}
className="form-control"
required="required"
placeholder={ingredients}
value={ingredients}
/>
<br />
<input
type="text"
onChange={e => setcookingTime(e.target.value)}
className="form-control"
required="required"
placeholder={cookingTime}
value={cookingTime}
/>
<br />
<input
type="text"
onChange={e => setsourceUrl(e.target.value)}
className="form-control"
required="required"
placeholder={sourceUrl}
value={sourceUrl}
/>
<br />
<input
type="text"
onChange={e => setimageUrl(e.target.value)}
className="form-control"
required="required"
placeholder={imageUrl}
value={imageUrl}
/>
<br />
<input
type="text"
onChange={e => setisPublished(e.target.value)}
className="form-control"
required="required"
placeholder={isPublished}
value={isPublished}
/>
<br />
<input
type="text"
onChange={e => setprice(e.target.value)}
className="form-control"
required="required"
placeholder={price}
value={price}
/>
<br />
<input
type="text"
onChange={e => settags(e.target.value)}
className="form-control"
required="required"
placeholder={tags}
value={tags}
/>
<br />
<button onClick={update} className="btn btn-primary">
Submit
</button>
</div>
);
}
Here is put from REST API:
router.put("/:id", async (req, res) => {
const { error } = validateCourse(req.body);
if (error)
//400 Bad request
return res.status(400).send(error.details[0].message);
const course = await Course.findByIdAndUpdate(
req.params.id,
_.pick(req.body, [
`dishName`,
`category`,
`password`,
`ingredients`,
`cookingTime`,
`sourceUrl`,
`imageUrl`,
`isPublished`,
`price`,
`tags`,
]),
{
useFindAndModify: false,
new: true,
}
);
if (!course)
return res.status(404).send(`The course with the given ID was not found`);
res.send(course);
});
And here is sample course from MongoDB:
{
"isPublished": true,
"tags": [
"pizza"
],
"_id": "60ae108ddfb18463c046a5ba",
"dishName": "Pizza with Cauliflower Crust",
"category": "pizza",
"ingredients": [
{
"_id": "60a4cfa48c20aa5c18517606",
"quantity": 1,
"unit": "",
"description": "medium head cauliflower cut into florets"
},
{
"_id": "60a4cfa48c20aa5c18517607",
"quantity": 1,
"unit": "",
"description": "egg"
},
{
"_id": "60a4cfa48c20aa5c18517608",
"quantity": 0.5,
"unit": "cup",
"description": "mozzarella shredded"
},
{
"_id": "60a4cfa48c20aa5c18517609",
"quantity": 1,
"unit": "tsp",
"description": "oregano or italian seasoning blend"
},
{
"_id": "60a4cfa48c20aa5c1851760a",
"quantity": null,
"unit": "",
"description": "Salt and pepper to taste"
},
{
"_id": "60a4cfa48c20aa5c1851760b",
"quantity": 1,
"unit": "cup",
"description": "chicken cooked and shredded"
},
{
"_id": "60a4cfa48c20aa5c1851760c",
"quantity": 0.5,
"unit": "cup",
"description": "barbecue sauce"
},
{
"_id": "60a4cfa48c20aa5c1851760d",
"quantity": 0.75,
"unit": "cup",
"description": "mozzarella shredded"
},
{
"_id": "60a4cfa48c20aa5c1851760e",
"quantity": null,
"unit": "",
"description": "Red onion to taste thinly sliced"
},
{
"_id": "60a4cfa48c20aa5c1851760f",
"quantity": null,
"unit": "",
"description": "Fresh cilantro to taste"
}
],
"cookingTime": 60,
"sourceUrl": "https://www.closetcooking.com/cauliflower-pizza-crust-with-bbq/",
"imageUrl": "https://www.closetcooking.com/wp-content/uploads/2013/02/BBQ-Chicken-Pizza-with-Cauliflower-Crust-500-4699.jpg",
"price": 29.99,
"date": "2021-05-26T09:10:37.620Z",
"__v": 0
}

When button is clicked and the PUT request is made and the values are updated - you must also tell your component states to mirror the new changes from the backend.
Therefor you must call old() method (which handles the fetching request and set states) after your PUT request. This is makes sure that your component states is sync with the values from the database.
Here is a small modification to your update() method (I marked it with an arrow):
async function update() {
let item = {
dishName,
category,
author,
ingredients,
cookingTime,
sourceUrl,
imageUrl,
isPublished,
price,
tags,
};
console.log(item);
console.log(JSON.stringify(item));
const result = await fetch(`http://localhost:1234/api/courses/${id}`, {
method: "PUT",
body: JSON.stringify(item),
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
console.log(result);
if (result.status !== 200) {
return setAlert(result.status + " " + result.statusText);
}
history.push("/");
window.location.reload();
old() <----- THIS
}
UPDATE:
I made a small example in Codesandbox that help you display ingredients and change their values.
I havent used any UI libraries but i strongly recommend something like Material UI to implement the containers and display them in a more proper way -it is all up to you how you want to design it.
Well here is it: https://codesandbox.io/s/cocky-sammet-d8wq5?file=/src/App.js

Related

2checkout Payment Authorization Failed in React

I have been using 2checkout payment gateway. I am using the script file provided by 2checkout. First I was importing it in my index.html file by using <link></link> and it was giving me an CORBS error. So I downloaded the script and place in local file.
Now, it's working as expected. It's providing me with authorization token by 2checkout. Following is my component that I have been using to get token form server.
import React, { useEffect, useState } from 'react';
const Form = (props) => {
const [ card, setCard ] = useState({
sellerId: <my-seller-id>,
publishableKey: <my-publishable-key>,
ccNo: '',
expMonth: '',
expYear: '',
cvv: ''
});
const [ returnToken, setReturnToken ] = useState(null);
useEffect(() => {
window.TCO.loadPubKey('sandbox');
}, []);
const submitted = (e) => {
e.preventDefault();
var payWithCard = (data) => {
console.log(data.response.token.token);
};
var error = (error) => {
console.log(error);
};
try {
window.TCO.requestToken(payWithCard, error, card);
} catch (error) {
setTimeout(() => {
window.TCO.requestToken(payWithCard, error, card);
}, 3000);
}
};
const change = (e) => {
setCard({
...card,
[e.target.name]: e.target.value
});
};
return (
<form id="tcoCCForm" onSubmit={submitted}>
<input id="sellerId" type="hidden" value={card.sellerId} />
<input id="publishableKey" type="hidden" value={card.publishableKey} />
<div>
<label>
<span>Card Number</span>
<input
id="ccNo"
name="ccNo"
type="text"
value={card.ccNo}
autoComplete="off"
required
onChange={(e) => change(e)}
/>
</label>
</div>
<div>
<label>
<span>Expiration Date (MM/YYYY)</span>
<input
type="text"
size="2"
id="expMonth"
name="expMonth"
value={card.expMonth}
required
onChange={(e) => change(e)}
/>
</label>
<span> / </span>
<input
type="text"
size="4"
id="expYear"
name="expYear"
value={card.expYear}
required
onChange={(e) => change(e)}
/>
</div>
<div>
<label>
<span>CVC</span>
<input
id="cvv"
name="cvv"
type="text"
value={card.cvv}
autoComplete="off"
required
onChange={(e) => change(e)}
/>
</label>
</div>
<input type="submit" />
</form>
);
};
export default Form;
so, it's giving me the token to console that I am using in Postman for testing the 2checkout api.
https://www.2checkout.com/checkout/api/1/<seller_id>/rs/authService
I have been using following payload to send the POST request to this api.
{
"sellerId": <seller_id>,
"privateKey": <private_key>,
"merchantOrderId": "123",
"token": "N2Y5MDFmNTItYzcxMS00OGQ5LTk2MmItOGJlMjAzYWQwNDFl",
"currency": "USD",
"demo": true,
"lineItems": [
{"name": "Package A", "price": 10, "quantity": 1, "type": "product", "recurrence": "1 Month", "duration": "Forever"} ],
"billingAddr": {"name": "Wasi Ullah", "addrLine1": " village Bharaj P/O Lakhanwal", "city": "Gujrat", "state": "Pubjab", "zipCode": "50700", "country": "Pakistan", "email": "chwasiullah#gmail.com", "phoneNumber": "+923006242851"}
}
While the response I got everytime is:
{
"validationErrors": null,
"response": null,
"exception": {
"exception": false,
"httpStatus": "400",
"errorMsg": "Payment Authorization Failed: Please verify your information and try again, or try another payment method.",
"errorCode": "607"
}
}
Even I have provided with original card and all information in demo mode. But there's still the same issue.
I got the solution to this problem. I want to share it. May be it will be helpful for you.
If you are testing 2checkout don't forget to check the documentation of test orders:
https://knowledgecenter.2checkout.com/Documentation/09Test_ordering_system/01Test_payment_methods
Moreover, I wasn't adding the name according to this test order in api that's why it was saying me that Card Authorization failed.

how can I store 4input data to anoptions array?

this is my react code where I want to create polls using react form but I don't understand what goes wrong with my code...!`Here I have 4 input field with { option1: "", option2: "", option3: "", option4: "" }, but I don't know how to store data just like I store data using POSTMAN...!CAN ANYONE HELP PLEASE help....!PLEASE HEPL I DO NOT UNDERSTAND WHAT TO DO...! WITH MY CODE
HELP PLEASE......
import React, { useState, useEffect } from "react";
import "../styles.css";
import { isAutheticated } from "../auth/helper/index";
import { createaPoll } from "./helper/adminapicall";
const AddPoll = () => {
const { user, token } = isAutheticated();
const [value, setValue] = useState({
question: "",
options: { option1: "", option2: "", option3: "", option4: "" },
error: "",
loading: "false",
getRedirect: false,
formData: "",
});
const { question, options, error, loading, getRedirect, formData } = value;
const handleChange = (event) => {
// setError("");
setValue({ ...value, [event.target.name]: event.target.value });
// console.log(event.target.value);
const newOption = {
...value.options,
[event.target.name]: event.target.value,
};
setValue((prev) => ({ ...prev, options: newOption }));
};
const onSubmit = (event) => {
event.preventDefault();
setValue({ ...value, error: "", loading: true });
// console.log(handel);
createaPoll(user._id, token, { question, options }).then((data) => {
if (data.error) {
setValue({ ...value, error: data.error });
} else {
setValue({
...value,
question: "",
options: { option1: "", option2: "", option3: "", option4: "" },
error: "",
loading: "false",
getRedirect: false,
formData: "",
});
}
});
};
return (
<div className="AddPoll">
<div className="container">
<h1>Add New Poll</h1>
<form>
<textarea
rows="4"
cols="50"
className="form-control mb-2"
placeholder="Question"
name="question"
value={question}
onChange={(event) => handleChange(event)}
autoFocus
></textarea>
<input
type="text"
className="form-control mb-2"
placeholder="Option1"
onChange={(event) => handleChange(event)}
name="option1"
value={options.option1}
/>
<input
type="text"
className="form-control mb-2"
placeholder="Option2"
onChange={(event) => handleChange(event)}
name="option2"
value={options.option2}
/>
<input
type="text"
className="form-control mb-2"
placeholder="Option3"
onChange={(event) => handleChange(event)}
name="option3"
value={options.option3}
/>
<input
type="text"
className="form-control mb-2"
placeholder="Option4"
onChange={(event) => handleChange(event)}
name="option4"
value={options.option4}
/>
<button type="submit" onClick={onSubmit} className="btn Submitbtn">
Submit
</button>
</form>
</div>
</div>
);
};
export default AddPoll;
And when I did with POSTMAN it's work fine! Here is my POSTMAN IMAGE
so I don't understand what to do, with my react form code , can anyone help me please....!
Try to separate question and option values in handleChange function.
const handleChange = event => {
if (event.target.name === 'question') {
setValue({ ...value, [event.target.name]: event.target.value });
} else {
const newOption = {
...value.option,
[event.target.name]: event.target.value
};
setValue({ ...value, option: newOption });
}
};
try this :
const [value, setValue] = useState({
question: "",
option: [ option1: "", option2: "", option3: "", option4: "" ],
error: "",
loading: "false",
getRedirect: false,
formData: "",
});
const onSubmit = (event) => {
event.preventDefault();
setValue({ ...value, error: "", loading: true });
// console.log(handel);
createaPoll(user._id, token, { question, option }).then((data) => {
if (data.error) {
setValue({ ...value, error: data.error });
} else {
setValue({
...value,
question: "",
option: ["hello","hi","hey"],
error: "",
loading: "false",
getRedirect: false,
formData: "",
});
}
});
};

Render JSON with sub object using React

Maybe someone there knows, how can I render "department" object from JSON?
[
{
"id": 1,
"name": "one",
"department": {
"id": 1,
"name": "development"
}
},
{
"id": 2,
"name": "two",
"department": {
"id": 2,
"name": "testing"
}
}
]
I am trying to display the data such that It's my render
render() {
const title =<h3>Employee</h3>;
const {Employees, isLoading} = this.state;
if (isLoading)
return(<div>Loading....</div>);
let rows=
Employees.map( employee =>
<tr key={employee.id}>
<td>{employee.id}</td>
<td>{employee.name}</td>
<td>{employee.department.name}</td>
<td><Button size="sm" color="danger" onClick={() => this.remove(employee.id)}>Delete</Button></td>
</tr>);
return {rows};
Tanx very much!
render() {
const title =<h3>Employee</h3>;
const {Employees, isLoading} = this.state;
if (isLoading)
return(<div>Loading....</div>);
let rows=
Employees.map( employee => {
return `<tr key=${employee.id}>
<td>${employee.id}</td>
<td>${employee.name}</td>
<td>${employee.department.name}</td>
<td><Button size="sm" color="danger" onClick=${() => this.remove(employee.id)}>Delete</Button></td>
</tr>` });
return {rows};
I fixed id! I needed to my back end code modify some... from "department" to private String departmentName;
and front
let rows=
Employees.map( employee =>
<tr key={employee.id}>
<td>{employee.id}</td>
<td>{employee.name}</td>
<td>{employee.department.departmentName}</td>
<td><Button size="sm" color="danger" onClick={() => this.remove(employee.id)}>Delete</Button></td>
</tr>);

How to add a key value pair on existing object created via Vue Reactivity

I already have an existing form which is dynamically created. However, I have problems with regards to adding a new set of key value pairs to the existing object. I have used the Vue Reactivity using the this.$set() method with success on the FIRST pair only.
Output
{ "traveller_1": { "gender": "c" },
"traveller_2": { "gender": "f" },
"traveller_3": { "gender": "i" }
}
Expected Output
{ "traveller_1": { "firstname": "John", "age": "23", "gender": "m" },
"traveller_2": { "firstname": "Jane", "age": "21", "gender": "f" },
"traveller_3": { "firstname": "Jade", "age": "25", "gender": "f" },
}
Fiddle https://jsfiddle.net/stda7Lwm/
View
<div class="col-md-10" id="app"> {{ travellerDetails }}
<div class="form-row" v-for="i in travellers">
<div class="form-group col-md-6" v-for="(details, index) in bookingRequiredDetails">
<label for="required-details">{{ details }}</label>
<input
type="text"
class="form-control"
#input="prop('traveller_' + i, details, $event)"
placeholder="Required Details"
/>
</div>
</div>
</div>
JS
new Vue({
el: '#app',
mounted () {
},
data () {
return {
test: { 'unit1' : { life: 30}},
travellerDetails: { },
travellers: 3,
bookingRequiredDetails: ['fullname', 'age', 'gender'],
};
},
methods: {
prop: function(obj, prop, event) {
this.$set(this.travellerDetails, obj, { [prop] : event.target.value } );
console.log(this.travellerDetails);
}
},
})
You're overriding all object every time you assign new value. You should change a single prop only
prop: function(obj, prop, event) {
const data = this.travellerDetails[obj] || {}
data[prop] = event.target.value
this.travellerDetails = {
...this.travellerDetails,
[obj]: {...data}
}
}

build schema correct in mongoDB/mongoose

I'm trying to make a poll vote app.
My big problem is that I can't make an array of objects and access them. I try to make a few option votes to one poll like that:
title:"question?"
option:content:"1 answer",vote:0
content:"2 answer",vote:0
content:"3 answer",vote:...
and build schema like that:
var mongoose = require("mongoose");
var pollSchema = new mongoose.Schema({
title: String,
maker :{
id:{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
options:[{content: String,vote:{type:Number,default:0}}]
});
module.exports = mongoose.model("Poll",pollSchema);
and get the data from there:
<div class="form-group" id="option">
<label for="Option 1">Options</label>
<input type="text" class="form-control" name="options[content]" placeholder="Option 1">
<input type="text" class="form-control" name="options[content]" placeholder="Option 2">
</div>
and the output in mongoDB is:
{
"_id": {
"$oid": "5993030dc5fa8c1b4f7eb176"
},
"title": "Some question?",
"options": [
{
"content": "opt 1,opt 2",
"_id": {
"$oid": "5993030dc5fa8c1b4f7eb177"
},
"vote": 0
}
],
"maker": {
"username": "Naor Malca"
},
"__v": 0
}
I want each option to have separate content, id and vote.
maybe i input the data worng?or the schema is worng?
UPDATE:
This my route code:
//NEW POLL ROUTE
app.post("/",function(req, res){
var title = req.body.title;
var options = req.body.options;
var maker = req.body.maker;
var newPoll = {title:title,options:options,maker:maker};
Poll.create(newPoll,function(err,poll){
if(err){
res.send("create error");
} else{
res.redirect("/");
}
});
});
still not working this the output:(its make the text an array not a array of objects...)
{ _id: 5993fcad9a63350df274b3e5,
title: '111',
__v: 0,
options: [ { text: '222,333', _id: 5993fcad9a63350df274b3e6, vote: '0' } ],
maker: { username: 'naor' } }
When you build your form, add [number] so that each option is different.
<div class="form-group" id="option">
<label for="Option 1">Options</label>
<input type="text" class="form-control" name="options[0][content]" placeholder="Option 1" value="value1">
<input type="text" class="form-control" name="options[1][content]" placeholder="Option 2" value="value2">
</div>
That way when you submit the form, it should come back as array of objects
req.body.options = [
{ content: 'value1' },
{ content: 'value2' }
]
This is a typical approach when you build an "add" form containing grouped elements.
--
When it comes to an "edit" form such that you want to update existing options, you can pass the option's _id
Here's an example using EJS
<div class="form-group" id="option">
<label for="Option 1">Options</label>
<% poll.options.forEach(option => { %>
<input type="text" class="form-control" name="options[<%= option._id %>][content]" placeholder="Option 1" value="<%= option.content %>">
<% }) %>
</div>
Here req.body.options should be an object
req.body.options = {
'some-id': {
content: 'value1'
},
'another-id': {
content: 'value2'
}
}
When you save, you would iterate over the object with the key i.e. _id. I think you can make use of id() to find the object to set.
--
If it doesn't come back in the formats mentioned above, you most likely need to use and set the bodyParser.

Resources