Why are vote options not correctly pre-selected? [closed] - node.js

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
You need to be authenticated to vote on a poll. When you vote on a poll, there are 2 issues:
You can vote an infinite number of times until you leave or reload the page
When you reload the page, you are finally prevented from voting but instead of having the option you voted on pre-selected, it's always the second option that is preselected.
WHAT SHOULD HAPPEN:
Sign up/ Login, then vote on a poll. The moment you click on an option, the poll voting choice you made is locked and you can't vote more on that poll.
HOW THE FLOW OF MY CURRENT CODE WORKS:
When a user clicks on an option, the counter is increased and the vote is then saved in an array that is pushed to the User object in the database.
When the component loads, the database data for votes for the currently logged in user is saved through the ngOninit() method inside the local votes variable which is then used to check on which poll the user already voted and what vote he made. The issue is that the choice is made is always choice2 when that is not actually the case.
I understand why you can vote many times until the page reloads, but I just don't know how to immediately lock the poll after the user voted, on the client and on the backend (prevent more votes from being registered if user already voted on poll).
As for why it is already the second choice that is pre-selected, I have no idea.
CODE:
HTML
<div class="formWidth">
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
<div class="form-group">
<label class="inputTitle" for="title">Poll Title</label>
<input
type="text"
id="title"
class="form-control"
[ngModel]="poll?.title"
name="title"
required maxlenth="30">
<label class="inputTitle" for="choice1">Choice1</label>
<input
type="text"
id="choice1"
class="form-control"
[ngModel]="poll?.choice1"
name="choice1"
required maxlenth="20">
<label class="inputTitle" for="choice2">Choice2</label>
<input
type="text"
id="choice2"
class="form-control"
[ngModel]="poll?.choice2"
name="choice2"
required maxlenth="20">
</div>
<button type="button" class="btn btn-danger" (click)="onClear(f)">Clear</button>
<button class="btn btn-primary" type="submit">Save</button>
</form>
</div>
COMPONENT
export class PollComponent {
#Input() poll: Poll;
constructor(private pollService: PollService) {}
votes: any;
// Pie
public pieChartLabels:string[] = [];
public pieChartData:number[] = [];
public pieChartType:string = 'pie';
public pieChartOptions:any = {};
ngOnInit() {
var result1 = parseFloat(((this.poll.counter1/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
var result2 = parseFloat(((this.poll.counter2/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
this.pieChartData = [result1, result2];
this.pieChartLabels = [this.poll.choice1, this.poll.choice2];
this.pieChartType = 'pie';
this.pieChartOptions = {
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label: function(tooltipItem, data) {
var allData = data.datasets[tooltipItem.datasetIndex].data;
var tooltipLabel = data.labels[tooltipItem.index];
var tooltipData = allData[tooltipItem.index];
return tooltipLabel + ": " + tooltipData + "%";
}
}
}
}
this.pollService.voted(localStorage.getItem('userId')).subscribe(
data => {
var result = JSON.parse(data);
this.votes = result.votes;
},
err => { console.log("NGONINIT ERROR: "+ err) },
() => { }
);
}
onEdit() {
this.pollService.editPoll(this.poll);
}
onDelete() {
this.pollService.deletePoll(this.poll)
.subscribe(
result => console.log(result)
);
}
onChoice1() {
this.pollService.increaseCounter1(this.poll);
this.onVote1();
var result1 = parseFloat(((this.poll.counter1/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
var result2 = parseFloat(((this.poll.counter2/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
this.pieChartData = [result1, result2];
}
onChoice2() {
this.pollService.increaseCounter2(this.poll);
this.onVote2();
var result1 = parseFloat(((this.poll.counter1/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
var result2 = parseFloat(((this.poll.counter2/(this.poll.counter2+this.poll.counter1))*100).toFixed(2));
this.pieChartData = [result1, result2];
}
onVote1() {
this.pollService.voteOn(this.poll.pollID, localStorage.getItem('userId'), 1);
}
onVote2() {
this.pollService.voteOn(this.poll.pollID, localStorage.getItem('userId'), 2);
}
belongsToUser() {
return localStorage.getItem('userId') == this.poll.userId;
}
alreadyVotedFor(choice: number) {
var result = "";
if (this.votes) {
for (var i = 0; i < this.votes.length; i ++) {
if (this.votes[i].pollID == this.poll.pollID) {
result = "disabled";
if (this.votes[i].choice == choice) {
result = "selected";
}
}
}
}
return result;
}
// events
public chartClicked(e:any):void {
}
public chartHovered(e:any):void {
}
}
SERVICE
updatePoll(poll: Poll) {
const body = JSON.stringify(poll);
const token = localStorage.getItem('token')
? localStorage.getItem('token')
: '';
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer '+token
});
return this.http.patch('https://voting-app-10.herokuapp.com/poll/' + poll.pollID, body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
});
}
increaseCounter1(poll: Poll) {
poll.counter1++;
const body = JSON.stringify(poll);
const token = localStorage.getItem('token')
? localStorage.getItem('token')
: '';
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer '+token
});
this.http.patch('https://voting-app-10.herokuapp.com/poll/vote/' + poll.pollID, body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
})
.subscribe();
}
increaseCounter2(poll: Poll) {
poll.counter2++;
const body = JSON.stringify(poll);
const token = localStorage.getItem('token')
? localStorage.getItem('token')
: '';
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer '+token
});
return this.http.patch('https://voting-app-10.herokuapp.com/poll/vote/' + poll.pollID, body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
})
.subscribe();
}
voteOn(pollID: string, userID: string, choice: number) {
var user;
this.http.get('https://voting-app-10.herokuapp.com/user/'+userID)
.map(response => response.json())
.subscribe(
json => {
user = JSON.parse(json);
if (user.votes == undefined) {
user.votes = [{pollID, choice}];
} else {
user.votes.push({pollID, choice});
}
const body = user;
const token = localStorage.getItem('token')
? localStorage.getItem('token')
: '';
const headers = new Headers({
'Content-Type': 'application/json',
'Authorization': 'Bearer '+token
});
return this.http.patch('https://voting-app-10.herokuapp.com/user/', body, {headers: headers})
.map((response: Response) => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
})
.subscribe();
}
)
}
voted(userID: string) {
const headers = new Headers({'Content-Type': 'application/json'});
return this.http.get('https://voting-app-10.herokuapp.com/user/'+userID,{headers: headers})
.map(response => response.json())
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
});
}
ROUTE (BACKEND)
router.patch('/vote/:id', function (req, res, next) {
var decoded = jwt.decode(getToken(req));
Poll.findById(req.params.id, function (err, poll) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
if (!poll) {
return res.status(500).json({
title: 'No Poll Found!',
error: {poll: 'Poll not found'}
});
}
poll.title = req.body.title;
poll.choice1 = req.body.choice1;
poll.choice2 = req.body.choice2;
poll.counter1 = req.body.counter1;
poll.counter2 = req.body.counter2;
poll.save(function (err, result) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
res.status(200).json({
poll: 'Updated poll',
obj: result
});
});
});
});
router.patch('/:id', function (req, res, next) {
var decoded = jwt.decode(getToken(req));
Poll.findById(req.params.id, function (err, poll) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
if (!poll) {
return res.status(500).json({
title: 'No Poll Found!',
error: {poll: 'Poll not found'}
});
}
if (poll.user != decoded.user._id) {
return res.status(401).json({
title: 'Not Authenticated',
error: {poll: 'Users do not match'}
});
}
poll.title = req.body.title;
poll.choice1 = req.body.choice1;
poll.choice2 = req.body.choice2;
poll.counter1 = req.body.counter1;
poll.counter2 = req.body.counter2;
poll.save(function (err, result) {
if (err) {
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
res.status(200).json({
poll: 'Updated poll',
obj: result
});
});
});
});

Okay, at first your radio buttons don't get disabled, because you don't update the votes-array in your poll.component.ts after saving a vote.
I'm not sure if it's a good solution or not:
In your poll.service.ts:
voteOn(pollID: string, userID: string, choice: number) {
var user;
return new Promise((resolve) => { //Create a new promise to wrap the Subscriptions
this.http.get('http://localhost:3000/user/' + userID)
.map(response => response.json())
.subscribe(
json => {
user = JSON.parse(json);
if (user.votes == undefined) {
...
.catch((error: Response) => {
this.errorService.handleError(error.json());
return Observable.throw(error);
}).subscribe(() => {
resolve(user.votes); // <- Resolve your promise
})
}
)
});
}
And in your poll.component.ts:
voteOn(...).then((votes) => {
this.votes = votes; // To update your votes array
this.updateVote();
})
And I don't recommend to call functions in bindings, because it happens that the functions are called very often "to detect changes", like in your components.
So I would change the code in following way:
In your poll.component.ts:
vote:any //Added to your poll component
updateVote() {
this.vote = this.votes.find((vote) => {
return vote.pollID === this.poll.pollID;
});
}
You need to call this method in your ngOnInit method:
this.pollService.voted(localStorage.getItem('userId')).subscribe(
data => {
var result = JSON.parse(data);
this.votes = result.votes;
this.updateVote(); // <- To select the vote of this poll
},
err => { console.log("NGONINIT ERROR: " + err) },
() => { }
);
And in your poll.component.html:
<fieldset [disabled]="vote">
{{ poll.counter1 }} votes <input type="radio" id="{{ poll.choice1 }}" name="my_radio" value="{{ poll.choice1 }}" (click)="onChoice1(form)" [checked]="vote?.choice == 1"> {{ poll.choice1 }}
<br>
{{ poll.counter2 }} votes <input type="radio" id="{{ poll.choice2 }}" name="my_radio" value="{{ poll.choice2 }}" (click)="onChoice2(form)" [checked]="vote?.choice == 2"> {{ poll.choice2 }}
</fieldset>
But if you don't want to change your code in such a way, please tell me, so i can provide another solution.

Related

React use axios return Axios Error {message: 'Network Error'} and TypeError: Cannot read properties of undefined (reading 'path')

when I click submit button, then web and terminal will return error like me title
but i try postman is ok , so i think is my axios setting error,how can i fixed this error? I found many similar questions, but can't not help me
the other question is , my form tag action is "/addItems", but i sending request , i got this error CANNOT POST / addItems Post http://localhost:3000/addItems 404 (Not Found)
(axios setting )
post(id, title, description, price, avatar) {
let token;
if (localStorage.getItem("user")) {
token = JSON.parse(localStorage.getItem("user")).token;
} else {
token = "";
}
const formData = new FormData();
// formData.append("id", id);
// formData.append("title", title);
// formData.append("description", description);
// formData.append("price", price);
formData.append("avatar", avatar);
return axios.post(
API_URL + "/addItems",
{ formData },
{
headers: {
Authorization: token,
"Content-Type": "multipart/form-data"
}
}
);
}
(item.route)
itemRouter.post("/addItems", upload.single("avatar"), async (req, res) => {
let { id, title, description, price, avatar } = req.body;
if (req.user.isMember()) {
return res.status(400).send("Only admin can add new items");
}
console.log(req.file);
avatar = req.file.path;
const newItem = new Item({
id,
title,
description,
price,
avatar
});
try {
await newItem.save();
console.log(req.file);
res.status(200).send("New item has been saved.");
} catch (err) {
res.status(400).send("Error");
console.log(err);
}
});
(addItemsComponent)
const handleChangePost = () => {
if (currentUser.user.role !== "admin") {
window.alert("Member can't not post item!! ");
navigate("/");
} else {
ItemService.post(avatar)
.then(() => {
window.alert("Post successfully");
navigate("/");
})
.catch((error) => {
console.log(error);
console.log(error.response);
setErrorMessage(error.response.data);
});
}
};
return (
<div>
<form action="/addItems" method="post" enctype="multipart/form-data">
<input onChange={handleChangeAvatar} value={avatar} type="file" name="avatar" />
<button type="submit" onClick={handleChangePost}>
Submit
</button>
</form>
</div>
);
Try this
return axios.post(API_URL + "/addItems", formData,
{
headers: {
Authorization: token,
"Content-Type": "multipart/form-data"
}
}
);

How do I send an image attachment via Email with Nodemailer in React?

I have asked this twice before two years ago and I still cannot get this to work. Let me explain.
How I want this to work is this:
User uploads some information in addition to an IMAGE file, NOT a text file.
I can easily send the text fields and receive them in an email.
I CANNOT receive the IMAGE file. It just appears blank with no file type or anything.
In the code, when the user hits the submit button, it is supposed to convert everything to a data form and upload that data to the backend.
However, while the text gets uploaded and sent, the file doesn't, so obviously I am missing something very fundamental here.
Do I actually need to upload the file to a physical file system like an HDD to then read the file on the server end? If so, how do I do that? Where do I save the file on the development side? How is this handled in production? Do I need a database for just one image file at a time? If I do need a database or some sort of physical storage, does that mean the flow would be to physically upload the file, read it, then delete it? Isn't that inefficient?
My last question can be seen here regarding the same current issue and I have yet to solve the problem: Why are my attachments not sending in Node.js?
What I want is to be able to see an image, NOT a blank attachment or a text file in my email attachment. I need an IMAGE file, a jpeg file specifically. How do I do this? What am I doing wrong? What buffer type am I supposed to use? The Nodemailer documentation has nothing that describes how to send an image, just all text files. Why won't this work? After two years on this I am at my wits end. Here again is my code, the relevant behavior is near the bottom of the server code:
FRONT END
import React, {Component} from "react";
import "./master.css";
import BuyHeader from "./images/buyBlocksHeader.png";
import LoginLabel from "./images/Label-Login.png";
import EmailLabel from "./images/Label-Email.png";
import ImageLabel from "./images/Label-Image.png";
import SubmitOff from "./images/Submit_Off.png";
import SubmitOn from "./images/Submit_On.png";
class Form extends Component {
constructor() {
super();
this.state = {
submitButton: SubmitOff,
_loginName: "",
_email: "",
_file: undefined,
};
}
baseURL = "http://localhost:8081/";
onLoginChange(event) {
this.setState({ _loginName: event.target.value })
}
onEmailChange(event) {
this.setState({ _email: event.target.value })
}
onFileChange(event) {
const file = event.target.value;
if (file){
this.setState({ _file: file});
}
console.log(file);
}
SendEmail = async(e) => {
e.preventDefault();
const formData = new FormData();
formData.append("loginName", this.state._loginName);
formData.append("email", this.state._email);
formData.append("file", this.state._file);
const response = await fetch(this.baseURL + "email", {
method: "POST",
mode: "cors",
body: formData,
});
const data = await response.json();
this.resetState();
console.log(data);
}
resetState() {
this.setState({_loginName: "", _email: "", _file: undefined});
this.props.handleFormState(false);
}
render () {
return (
<div>
<img src={BuyHeader} alt={""} />
<form onSubmit={this.SendEmail} encType="multipart/form-data">
<div>
<label htmlFor="loginName"><img src={LoginLabel} alt={""} /></label>
<input type="text" id="loginName" required value={this.state._loginName} onChange={this.onLoginChange.bind(this)}/>
</div>
<div>
<label htmlFor="email"><img src={EmailLabel} alt={""} /></label>
<input type="email" id="email" required value={this.state._email} onChange={this.onEmailChange.bind(this)} />
</div>
<div>
<label htmlFor="file"><img src={ImageLabel} alt={""} /></label>
<input type="file" id="file" required accept=".jpeg, .jpg" onChange={this.onFileChange.bind(this)} />
</div>
<div>
<button type="submit">
<img src={this.state.submitButton}
alt={""}
onMouseEnter={() => {
this.setState({ submitButton: SubmitOn });
}}
onMouseOut={() => {
this.setState({ submitButton: SubmitOff });
}}
/>
</button>
</div>
</form>
</div>
)
}
}
export default Form;
SERVER
const express = require("express")
const app = express();
const multer = require("multer");
const access = require("./config.json");
const {writeFile, readFileSync} = require("fs");
const accessPath = "./config.json";
require("dotenv").config();
const request = require("request");
const cors = require("cors");
const nodemailer = require("nodemailer");
const SMTPTransport = require("nodemailer/lib/smtp-transport");
app.use(cors({
origin: "*"
}));
app.use(express.json());
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header('Access-Control-Allow-Credentials', true);
next();
});
var file = access;
var PORT = file.port;
var server = app.listen(PORT, function() {
var port = server.address().port;
console.log("Back end listening at localhost:", port)
});
const fileData = readFileSync(accessPath, "utf8");
const jsonData = JSON.parse(fileData);
const upload = multer({
storage: multer.memoryStorage()
});
const directUpload = upload.fields([
{name: "loginName"},
{name: "email"},
{name: "file"}
]);;
const contactEmail = nodemailer.createTransport(new SMTPTransport({
name: "*****",
host: "*****",
port: ***,
secure: true,
auth: {
user: process.env.EMAIL,
pass: process.env.PASS,
},
}));
contactEmail.verify((error) => {
if (error) {
console.log(error);
} else {
console.log("Ready to send email request!");
}
});
var token = "";
var curToken = file.access_token;
var activeStreams = [];
var activeUser;
app.get('/', function(req, res) {
res.sendFile(__dirname + "/public/index.html");
});
/*Run this on initialization*/
function ValidateToken(currentToken) {
const options = {
url: process.env.VALIDATE_TOKEN,
json: true,
headers: {
"Client-ID": process.env.CLIENT_ID,
"Authorization": 'Bearer ' + currentToken,
}
};
request.get(options, (err, res, body) => {
if (err) {
return console.log(err);
}
console.log(res.statusCode);
if (res.statusCode !== 200) {
jsonData["access_token"] = "";
writeFile(accessPath, JSON.stringify(jsonData, null, 4), (error) => {
if (error) {
console.log("ERROR: File could not be written.", error);
}
console.log("File successfully written!");
})
const newFileData = readFileSync(accessPath, "utf8");
const data = JSON.parse(newFileData);
curToken = data.access_token;
}
})
}
ValidateToken(curToken);
function CheckToken(currentToken) {
if (currentToken === "") {
const GrabToken = (url, callback) => {
const options = {
url: process.env.GET_TOKEN,
json: true,
body: {
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
grant_type: "client_credentials",
}
};
request.post(options, (err, res, body) => {
if (err) {
return console.log(err);
}
console.log(body);
callback(res);
})
};
GrabToken(process.env.GET_TOKEN, (res) => {
token = res.body.access_token;
const newFileData = readFileSync(accessPath, "utf8");
const data = JSON.parse(newFileData);
data["access_token"] = token;
writeFile(accessPath, JSON.stringify(data, null, 4), (error) => {
if (error) {
console.log("ERROR: File could not be written.", error);
}
console.log("File successfully written!");
})
console.log(token);
curToken = token;
return curToken;
})
};
console.log(curToken);
}
setTimeout(() => {
CheckToken(curToken)
}, 1000);
function FindStream(currentToken) {
const options = {
url: process.env.GET_STREAMER + file.user,
json: true,
headers: {
"Client-ID": process.env.CLIENT_ID,
"Authorization": 'Bearer ' + currentToken,
}
};
request.get(options, (err, res, body) => {
if (err) {
return console.log(err);
}
activeStreams = res.body.data.map(obj=>
obj.user_login);
console.log(activeStreams);
})
}
setTimeout(() => {
FindStream(curToken)
}, 1500)
/*End initialization*/
app.post('/streams', (req, res) => {
function ValidateToken(currentToken) {
const options = {
url: process.env.VALIDATE_TOKEN,
json: true,
headers: {
"Client-ID": process.env.CLIENT_ID,
"Authorization": 'Bearer ' + currentToken,
}
};
request.get(options, (err, res, body) => {
if (err) {
return console.log(err);
}
console.log(res.statusCode);
if (res.statusCode !== 200) {
jsonData["access_token"] = "";
writeFile(accessPath, JSON.stringify(jsonData, null, 4), (error) => {
if (error) {
console.log("ERROR: File could not be written.", error);
}
console.log("File successfully written!");
})
const newFileData = readFileSync(accessPath, "utf8");
const data = JSON.parse(newFileData);
curToken = data.access_token;
}
})
}
ValidateToken(curToken);
function CheckToken(currentToken) {
if (currentToken === "") {
const GrabToken = (url, callback) => {
const options = {
url: process.env.GET_TOKEN,
json: true,
body: {
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
grant_type: "client_credentials",
}
};
request.post(options, (err, res, body) => {
if (err) {
return console.log(err);
}
console.log(body);
callback(res);
})
};
GrabToken(process.env.GET_TOKEN, (res) => {
token = res.body.access_token;
const newFileData = readFileSync(accessPath, "utf8");
const data = JSON.parse(newFileData);
data["access_token"] = token;
writeFile(accessPath, JSON.stringify(data, null, 4), (error) => {
if (error) {
console.log("ERROR: File could not be written.", error);
}
console.log("File successfully written!");
})
console.log(token);
curToken = token;
return curToken;
})
};
console.log(curToken);
}
setTimeout(() => {
CheckToken(curToken)
}, 1000);
function FindStream(currentToken) {
const options = {
url: process.env.GET_STREAMER + file.user,
json: true,
headers: {
"Client-ID": process.env.CLIENT_ID,
"Authorization": 'Bearer ' + currentToken,
}
};
request.get(options, (err, res, body) => {
if (err) {
return console.log(err);
}
activeStreams = res.body.data.map(obj=>
obj.user_login);
console.log(activeStreams);
})
}
setTimeout(() => {
FindStream(curToken)
}, 1500)
res.json(activeStreams);
})
app.get('/live', function(req, res) {
res.send(activeUser)
console.log("Received signal. " + activeUser);
});
app.post('/live', (req, res) => {
const {parcel} = req.body
activeUser = activeStreams.includes(parcel);
var userStatus = JSON.stringify(activeUser);
res.json(userStatus);
})
app.post('/email', directUpload, (req, res ) => {
const mailBody = {
from: req.file.email,
to: process.env.EMAIL,
subject: "The Block: Purchase Blocks Request",
html: `<p>Streamer Login Name: ${req.file.loginName}</p>
<p>Reply Email: ${req.file.email}</p>`,
attachments: [
{
__filename: "adImage.jpg", /*What should this be? The file name depends upon what the user uploaded*/
content: new Buffer.from(req.file.file, "base64"),
}
]
};
contactEmail.sendMail(mailBody, (error) => {
if (error) {
res.json({ status: "ERROR" });
} else {
res.json({ status: "Message sent!" });
}
});
})
I thank anyone who can provide a VERY DETAILED explanation of what I need to do, how to do it, what exactly I am doing wrong and why it is not working. I would love to finally be able to get this working. Thank you!!!

formidable error: bad content-type header, unknown content-type: text/plain;charset=UTF-8

I'm receiving this error: Error: bad content-type header, unknown content-type: text/plain;charset=UTF-8 when trying to upload a photo using Formidable. When I console.log fields and files, I receive two empty objects.
What do I need to change to solve this and upload the photo?
CreatePost.js
const CreatePost = () => {
const [values, setValues] = useState({
title: "",
body: "",
photo: "",
error: "",
createdPost: "",
formData: "",
});
const { user, token } = isAuthenticated();
const {
title,
body,
error,
createdPost,
formData,
} = values;
const handleChange = (name) => (event) => {
const value = name === "photo" ? event.target.files[0] : event.target.value;
setValues({ ...values, [name]: value, formData: new FormData() });
};
const clickSubmit = (event) => {
event.preventDefault();
setValues({ ...values, error: "" });
createPost(user._id, token, formData).then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
title: "",
body: "",
photo: "",
createdPost: data.title,
});
}
});
};
const newPostForm = () => (
<form className="mb-3" onSubmit={clickSubmit}>
<h4>Post Photo</h4>
<div className="form-group">
<label className="btn btn-secondary">
<input
onChange={handleChange("photo")}
type="file"
name="photo"
accept="image/*"
/>
</label>
</div>
<div className="form-group">
<label className="text-muted">Title</label>
<input
onChange={handleChange("title")}
type="text"
className="form-control"
value={title}
/>
</div>
<div>
<label>Post body</label>
<textarea
onChange={handleChange("body")}
value={body}
/>
</div>
<button>Create Post</button>
</form>
);
const showError = () => (
<div
style={{ display: error ? "" : "none" }}>
{error}
</div>
);
const showSuccess = () => (
<div
style={{ display: createdPost ? "" : "none" }}>
<h2>{`${createdPost} is created!`}</h2>
</div>
);
return (
<div>
<div>
{showSuccess()}
{showError()}
{newPostForm()}
</div>
</div>
);
};
export default CreatePost;
API request (create post)
export const createPost = (userId, post, token) => {
return fetch(`${API}/blog/post/${userId}`, {
method: 'POST',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
},
body: post
})
.then(response => {
return response.json();
})
.catch(err => {
console.log(err);
});
};
controllers/posts.js
exports.create = (req, res) => {
let form = new formidable()
form.keepExtensions = true
form.parse(req, (err, fields, files) => {
if(err) {
console.log(err)
return res.status(400).json({
error: 'Image could not be uploaded'
})
}
const { title, body } = fields
console.log(fields)
if (!title || !body) {
return res.status(400).json({
error: "All fields are required"
})
}
let post = new Post(fields)
if(files.photo) {
if (files.photo.size > 1000000) {
return res.status(400).json({
error: "Image should be less than 1MB in size."
})
}
post.photo.data = fs.readFileSync(files.photo.path)
post.photo.contentType = files.photo.type
}
post.save((err, result) => {
if(err) {
return res.status(400).json({
error: errorHandler(err)
})
}
res.json(result)
})
})
}
exports.photo = (req, res, next) => {
if (req.post.photo.data) {
res.set('Content-Type', req.post.photo.contentType)
return res.send(req.post.photo.data)
}
next()
}
this has been up for a long time, I bet you already solve it.
If somebody stumbles with this question as I did, what worked for me was adding .IncomingForm() method on the new form in post controller file, like this:
let form = new formidable.IncomingForm();

mern - updated values are null in data

I'm trying to update the posts. The PUT request in the back end works fine, returning 200 and updates posts when tested on Postman however when I try to update a post in the front end (react), I'm not receiving any errors but the updated post isn't being updated on submit and the updated fields (title and body) are null. The updated values are null when I console.log(data) in the front end which is why they aren't being sent to the back end but they are shown correctly in post.
Why are the updated values null inside data? How can I update the post with the new values instead of getting null?
data:
post:
Updated code: Frontend
const EditPost = ({match}) => {
const [values, setValues] = useState({
title: "",
body: "",
error: ""
});
const [post, setPost] = useState({});
const { user, token } = isAuthenticated();
const {
title,
body,
error,
} = values;
const init = (id) => {
read(id).then(data => {
if (data.error) {
setValues({...values, error: data.error})
} else {
setValues({...values,
title: data.title,
body: data.body,
})
setPost({title: values.title, body: values.body})
}
})
}
useEffect(() => {
const id = match.params.id;
init(id);
}, []);
useEffect(() => {
setPost({...values });
}, [values.title, values.body]);
const handleChange = (name) => (event) => {
setValues({ ...values, [name]: event.target.value });
};
const clickSubmit = (event) => {
event.preventDefault();
setValues({ ...values, error: "" });
editPost(match.params.userId, match.params.id, token, post).then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
title: "",
body: "",
error: false,
});
console.log(post)
console.log(data)
}
});
};
const newPostForm = () => (
<form onSubmit={clickSubmit}>
<div>
<input
onChange={handleChange("title")} type="text"
name="title"
value={title}
/>
</div>
<div className="form-group">
<textarea
onChange={handleChange("body")}
value={body} name="body"
/>
</div>
<button type="submit">Publish</button>
</form>
);
const showError = () => (
<div
style={{ display: error ? "" : "none" }}>
{error}
</div>
);
return (
<div>
{showError()}
{newPostForm()}
</div>
);
};
export default EditPost;
export const editPost = (userId, id, token, post) => {
return fetch(`${API}/${userId}/${id}/edit`, {
method: 'PUT',
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify(post)
})
.then(response => {
return response.json();
})
.catch(err => console.log(err));
};
postsByUser.js
<Link className="mypost_btn edit_btn" to={`/${_id}/${post._id}/edit`}>
Edit
</Link>
Backend code
exports.edit = (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send(`ID is not valid: ${req.params.id}`)
const {title, body} = req.body
const updatedPost = {title, body }
Post.findByIdAndUpdate(req.params.id, {
$set: updatedPost
}, {new:true}, (error, data) => {
if (error) {
return error
} else {
res.send(data)
console.log(data)
}
})
}
Your problem lies here:
editPost(match.params.userId, match.params.id, token, post)
post is not defined.
Since post is not defined, no data is passed. Hence title and body equal to null. What you will need to do is, assuming from what I'm seeing on your code, is to define a state variable called post. I think you intended to do that:
const [post, setPost] = useState({values.title, values.body});
Then ensure that your post is updated whenever your values change using useEffect(),
useEffect(() => {
setPost({...values });
}, [values.title, value.body]);
So by the time you call your editPost() http-put-method, then post has a value. And it should work.
in EditPost.js editPost(match.params.userId, match.params.id, token).then((data) => { here you are missing the 4th arg which is the "post" it self you send to be updated

How to send data from React front-end to Nodejs Express back-end on button click

Disclaimer: I am new to React and Nodejs and have of course looked at all the current similar posts but none of them were able to help me fully so please don't mark as duplicate.
In my Nodejs index.js I have:
app.get('/list-certs', function (req, res) {
fs.readFile("certs.json", 'utf8', function (err, data) {
console.log( data );
res.send( data );
});
})
app.post('/list-certs', function (req, res) {
res.send('POST request: ' + req.body.domain-input);
console.log('post is working!')
});
And in my React App.js I have:
componentDidMount() {
this.callApi()
.then(res => {
this.setState({
json: res.map(x => x),
})
})
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/list-certs');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
handleChange(event) {
this.setState({domainInputValue: event.target.value});
}
click() {
}
render() {
return (
<div className="App">
<form action="http://127.0.0.1:8000/list-certs" className="add-cert-form" method="post">
<h1>Add new domain</h1>
<h5 className="domain-label">Name:</h5>
<Input className="domain-input" value={this.state.domainInputValue} onChange={(e) => {this.handleChange(e)}}></Input>
<Button onClick={this.click} className="domain-button" type='primary'>Add</Button>
</form>
.
.
.
}
How do I send the data in my input field domain-input to my back-end when the button domain-button is clicked? I know something needs to go in the click() but I'm not sure what.
Any help is appreciated!
Your click function will look like it:
async click() {
const { domainInputValue } = this.state;
const request = await fetch('/echo/json', {
headers: {
'Content-type': 'application/json'
},
method: 'POST',
body: { domainInputValue }
});
}

Resources