Trying to update a form without uploading a new image. I'm
using multer for the image upload. it works very well when i
create a form.
I'm using reactJs for frontend and node for the server side.
This is the front end code with react
import React, { useState, useEffect } from 'react';
import { useParams } from "react-router-dom";
import useFetch from "./fetch";
function EditForm() {
const { id } = useParams();
const { contestant: form, error, isPending } =
useFetch("http://localhost:5000/dashboard/form_single/" +
id);
const [name, setName] = useState('');
const [address, setAddress] = useState('');
const [fileName, setFileName] = useState('');
useEffect(() => {
setName(form.full_name);
setAddress(form.home_address);
}, [form])
const editForm = async (id) => {
try {
const formData = new FormData();
formData.append("name", name);
formData.append( "home_address", address);
formData.append("image", fileName);
const myHeaders = new Headers();
myHeaders.append("jwtToken", localStorage.jwtToken);
await
fetch(`http://localhost:5000/dashboard/form_update/${id}`, {
method: "PUT",
headers: myHeaders,
body: formData,
});
} catch (err) {
console.error(err.message);
}
};
const onChangeFile = e => {
setFileName(e.target.files[0]);
}
return (
<div>
{ isPending && <div>Loading...</div> }
{ error && <div>{ error }</div> }
<form encType="multipart/form-data"
onSubmit={() => editForm(form.form_id)}>
<input
type="text"
className="form-control"
value={name || ''}
onChange={e => setName(e.target.value)}
/>
<input
type="text"
className="form-control"
value={address || ''}
onChange={e => setAddress(e.target.value)}
/>
<input
type="file"
id="update"
name="image"
onChange={onChangeFile}
/>
<button type ="submit" >Save</button>
</form>
<div>
<img
alt="contestant"
src= {`http://localhost:5000/upload/${form.passport}`}
className="rounded-circle" style={{width: "100px",
height: "100px"}}/>
</div>
</div>
);
}
export default EditForm;
UNFORTUNATELY I GET Cannot read properties of undefined (reading 'filename'). I've tried to make multer image upload optional but it did'nt work. The code bellow is the api.
This is the server side code. Nodejs
router.put("/form_update/:id", upload.single('image'), async(req,
res) => {
try {
const { id } = req.params;
const image = req.file.filename;
const { name, home_address } = req.body;
const updateForm = await pool.query("UPDATE form SET
full_name = $1, home_address = $2, passport = $3 WHERE
form_id = $4
RETURNING *", [name, home_address, image, id]);
res.json("Form was updated");
} catch (err) {
console.error(err.message);
}
});
how do i not always have to change image everytime i need to edit a form.
on the server, check if fields are provided, and then store values for each, and construct query dynamically, and when done checking, execute query
you could list fields in an array and then iterate them and construct query and values against req.body
you could also add some validation (check if there is id etc., you could add that on the front-end as well)
try this:
router.put("/form_update/:id", upload.single('image'), async(req, res) => {
try {
// fields. same as in req.body
const fields = ['full_name', 'home_address', 'something', 'else'];
// store values
const values = [];
// dynamic query string
let stmt = [];
const {id } = req.params;
// add some validation
if(!id) {
console.error('no id..');
return res.json({msg:"err: no id"});
}
const image = req.file ? req.file.filename : '';
// build query
fields.map((field)=>{
if(req.body[field]) {
values.push(req.body[field]);
stmt.push(`${field} = $${values.length}`);
}
});
// check image, as it's not in req.body
if(image) {
values.push(image);
stmt.push(`passport = $${values.length}`);
}
// no data..end
if(!values.length) {
console.log('no data..');
return res.json({msg:'no data..'});
}
// finish
stmt = "UPDATE form SET " + stmt.join(', ');
values.push(id);
stmt += ` WHERE form_id = $${values.length}`;
stmt += ' RETURNING *';
const updateForm = await pool.query(stmt, values);
res.json({msg:"Form was updated"});
} catch (err) {
console.error(err.message);
}
});
Related
I have a page in NextJS for editing an sql row and sending it back. I have fetched all the rows from the table and then have set the state to be the single row which matches the query parameter in the useRouter hook. Now, after I have edited the data in the row, what is a good way to POST it back to the backend?
Below is my React code:
import { React, useEffect, useState } from "react";
import { useRouter } from "next/dist/client/router";
const axios = require("axios");
export default function Edit() {
const [data, setData] = useState([]);
const router = useRouter();
const onSubmitHandler = (e) => {
e.preventDefault();
axios.post("/api/cards", data);
};
useEffect(() => {
const fetchData = async () => {
await axios
.get("/api/cards")
.then((res) => {
if (res.data) {
res.data.map((element) => {
if (element.ID == router.query.card) {
setData(element);
return;
}
return;
});
}
})
.catch((err) => {
console.log(err);
});
};
if (router.isReady) {
fetchData();
}
}, [router.isReady, router.query.card]);
return (
<form onSubmit={onSubmitHandler}>
<label htmlFor="front">Front</label>
<input
defaultValue={data.Front}
id="front"
onChange={(e) => setData({ ...data, Front: e.target.value })}
></input>
<label htmlFor="back">Back</label>
<input
defaultValue={data.Back}
id="back"
onChange={(e) => setData({ ...data, Back: e.target.value })}
></input>
<button type="submit">Add Word</button>
</form>
);
}
Below is my backend code
if (req.method === "POST") {
const { front, back, type } = req.body.data;
const id = uuidv4();
db.query(
`INSERT INTO deck VALUES('${front}', '${back}', '${type}', '${id}')`,
(err, rows, fields) => {
if (!err) {
res.json(rows);
} else {
console.log(err);
}
}
);
}
Its good to post the edited data after submiting the form..
const onSubmitHandler = async (e) => {
e.preventDefault();
try {
await axios.post("/api/cards", data);
// react-toast or something like that to indicate the ui the form is updated
// then the control flow of the application
} catch (error){
console.error(error)
}
};
One thing I notice over here is you're using POST for the update. Try HTTP PUT instead of POST.
And regarding your answer: You can send modified data in your API call like you're already maintaining the state of the updated data. Then you can just send that row to the API call and handled that in your backend code.
const onSubmitHandler = (e) => {
e.preventDefault();
axios.put("/api/cards/:id", data); // modify the API URL and append dynamic ID of the record.
};
I've been trying to figure this out for hours, but i think im doing it wrong. So what im basically doing is.
-> Fetch the specific user, and throw it into the placeholder -> which is working
but when I try to edit the information and then when i try to save it this error shows and then in my phpadmin, it saves a null. it seems that I can't connect the front and the backend.
sql: 'UPDATE users SET name = NULL, email = NULL, mobile = NULL WHERE id = ${id} '
Edit.jsx
import axios from 'axios'
import React, { useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
const EditUser = () => {
const navigate = useNavigate()
const [data, setData] = useState([])
const [inputs, setInputs] = useState([])
const { id } = useParams()
useEffect(() => {
const getUser = async () => {
const res = await axios.get(`http://localhost:5000/${id}`)
setData(res.data[0])
}
getUser()
}, [])
const handleSubmit = async () => {
try {
await axios.put(`http://localhost:5000/${id}`, inputs)
} catch (error) {
console.log(error)
}
}
const handleChange = (e) => {
setInputs({ ...inputs, [e.target.name]: e.target.value })
}
return (
<div>
<form
action=""
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
}}
onSubmit={handleSubmit}
>
<label htmlFor="">Name</label>
<input type="text" placeholder={data.name} onChange={handleChange} />
<label htmlFor="">Email</label>
<input type="text" placeholder={data.email} onChange={handleChange} />
<label htmlFor="">Mobile</label>
<input type="text" placeholder={data.mobile} onChange={handleChange} />
<button type="submit">save</button>
</form>
</div>
)
}
export default EditUser
backend.js
app.put('/:id', (req, res) => {
const id = req.params.id
const name = req.body.name
const email = req.body.email
const mobile = req.body.mobile
pool.query(
`UPDATE users SET name = ?, email = ?, mobile = ? WHERE id = ${id} `,
[name, email, mobile],
(err, result) => {
if (err) {
console.log(err)
} else {
res.send(result)
}
}
)
})
app.get('/:id', (req, res) => {
const { name, email, mobile } = req.body
const { id } = req.params
pool.query(`SELECT * FROM users WHERE id = ${id} `, (err, result) => {
if (err) {
console.log(err)
} else {
res.send(result)
}
})
})
May be you should try :
The following states :
const EditUser = () => {
const navigate = useNavigate()
const [data, setData] = useState([])
const [name, setName] = useState("")
const [email, setEmail] = useState("")
const [mobile, setMobile] = useState("")
const { id } = useParams()
For your request :
const handleSubmit = async () => {
let datas ={
name: name,
email: email,
mobile: mobile
}
try {
await axios.put('http://localhost:5000/updateUser/'+id, datas)
} catch (error) {
console.log(error)
}
}
Then you just update your states that way :
<form
action=""
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
}}
onSubmit={handleSubmit}
>
<label htmlFor="">Name</label>
<input type="text" placeholder={data.name} onChange={()=>
{setName(e.currentTarget.value)} />
<label htmlFor="">Email</label>
<input type="text" placeholder={data.email} onChange={()=>
{setEmail(e.currentTarget.value)} />
<label htmlFor="">Mobile</label>
<input type="text" placeholder={data.mobile} onChange={()=>
{setMobile(e.currentTarget.value)} />
<button type="submit">save</button>
</form>
For you back end maybe you should add some informations in the adress (to avoid confusion with you app.get):
app.put('updateUser/:id', (req, res) => {
const id = req.params.id
const name = req.body.name
const email = req.body.email
const mobile = req.body.mobile
pool.query(
`UPDATE users SET name = ?, email = ?, mobile = ? WHERE id = ${id} `,
[name, email, mobile],
(err, result) => {
if (err) {
console.log(err)
} else {
res.send(result)
}
}
)
})
I hope it will help you !
I think I did it actually, but I dont know if this is the right method. so what i did is
instead of directing submit it as an await
const handleSubmit = async () => {
try {
await axios.put(`http://localhost:5000/${id}`, inputs)
} catch (error) {
console.log(error)
}
}
i did this.
const handleSubmit = async (e) => {
e.preventDefault()
try {
const res = await axios.put(`http://localhost:5000/${id}`, inputs)
setInputs(res.data)
} catch (error) {
console.log(error)
}}
Hey guys, if its wrong, please show me the other way.
I'm working on a recipe site using API from a third party and want to change the number of servings (which is output from the API data) when clicking the + & - button. I tried assigning the output serving amount <Servings>{recipe.servings}</Servings> in a variable and useState to update it but it kept showing errors. I would appreciate any help (preferably using react Hooks). Thanks :)
Here is my code:
const id = 716429;
const apiURL = `https://api.spoonacular.com/recipes/${id}/information`;
const apiKey = "34ac49879bd04719b7a984caaa4006b4";
const imgURL = `https://spoonacular.com/cdn/ingredients_100x100/`;
const {
data: recipe,
error,
isLoading,
} = useFetch(apiURL + "?apiKey=" + apiKey);
const [isChecked, setIsChecked] = useState(true);
const handleChange = () => {
setIsChecked(!isChecked);
};
return (
<Section>
<h2>Ingredients</h2>
<ServingsandUnits>
{recipe && (
<ServingsIncrementer>
<p>Servings: </p>
<Minus />
<Servings>{recipe.servings}</Servings>
<Plus />
</ServingsIncrementer>
)}
<ButtonGroup>
<input
type="checkbox"
id="metric"
name="unit"
checked={isChecked}
onChange={handleChange}
/>
<label htmlFor="male">Metric</label>
</ButtonGroup>
</ServingsandUnits>
</Section>
};
My custom hook is called useFetch:
const useFetch = (url) => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortCont = new AbortController();
fetch(url, { signal: abortCont.signal })
.then((res) => {
if (!res.ok) {
// error coming back from server
throw Error("Could not fetch the data for that resource");
}
return res.json();
})
.then((data) => {
setIsLoading(false);
setData(data);
setError(null);
})
.catch((err) => {
if (err.name === "AbortError") {
console.log("Fetch aborted");
} else {
// auto catches network / connection error
setIsLoading(false);
setError(err.message);
}
});
return () => {
abortCont.abort();
};
}, [url]);
return { data, isLoading, error };
};
export default useFetch;
I'm new in React Native. I would like to extract a value from json with fetch to do a simple test to begin. But I don't understand, how to select a particular key from Json. Always, I have undefined return. I tried to modify my code with this post but it doesn't work. I tried to parse before but he didn't want because it's already an object.
This is my code:
checkLogin = () => {
const { name } = this.state;
const { surname } = this.state;
fetch('https://ffn.extranat.fr/webffn/_recherche.php?go=ind&idrch=' + name + '%20' + surname, {
method: 'GET',
}).then((response) => response.json())
.then((responseJson) => {
if (responseJson.ind == 'Individu non trouv\u00e9 !') {
alert("Id incorrect")
}
else {
alert("Id correct");
}
alert(JSON.stringify(responseJson.ind))
}).catch((error) => {
console.error(error);
});
}
This is my JSON format:
[{"iuf":"1366701","ind":"LEBRUN L\u00e9o (2000) H FRA - CN BEAUPREAU","sex":"#1e90ff","clb":"CN BEAUPREAU"}]
I know my request work because when I run this code alert(JSON.stringify(responseJson)).It return the entire json. So I don't know, how to resolve the undefined return.
Regards
Your json is an array, you either need to loop through it if there is multiple items inside, or just use responseJson[0] to read it. So if you want to read your json, your code would look like this :
const checkLogin = () => {
const { name } = this.state;
const { surname } = this.state;
fetch(
"https://ffn.extranat.fr/webffn/_recherche.php?go=ind&idrch=" +
name +
"%20" +
surname,
{
method: "GET"
}
)
.then(response => response.json())
.then(responseJson => {
// Since you have only one object inside your json, you can read the first item with 'responseJson[0]'.
if (responseJson[0].ind == "Individu non trouv\u00e9 !") {
alert("Id incorrect");
} else {
alert("Id correct");
}
alert(JSON.stringify(responseJson[0].ind));
// If you have multiple elements inside your responseJson,
// then here is a loop example :
// responseJson.forEach(item => {
// console.log('item ind = ', item.ind);
// });
})
.catch(error => {
console.error(error);
});
};
Use async await.
const checkLogin = async () => {
const { name } = this.state;
const { surname } = this.state;
const request = await fetch(
"https://ffn.extranat.fr/webffn/_recherche.php?go=ind&idrch=" +
name +
"%20" +
surname)
const response = await request.json();
console.log('result from server', response)
}
This is my code in App.js, and its always returning an "Unhandeled Rejection Type error saying that ipfs.add(...). then is not a function.
import React, { Component } from 'react';
import './App.css';
var ipfsAPI = require('ipfs-http-client')
var ipfs = ipfsAPI({host: 'localhost', port: '5001', protocol:'http'})
class App extends Component {
saveTestBlobOnIpfs = (blob) => {
return new Promise(function(resolve, reject) {
const descBuffer = Buffer.from(blob, 'utf-8');
ipfs.add(descBuffer).then((response) => {
console.log(response)
resolve(response[0].hash);
}).catch((err) => {
console.error(err)
reject(err);
})
})
}
render() {
return (
<div className="App">
<h1>IPFS Pool</h1>
<input
ref = "ipfs"
style = {{width: 200, height: 50}}/>
<button
onClick = {() => {
console.log("Upload Data to IPFS");
let content = this.refs.ipfs.value;
console.log(content);
this.saveTestBlobOnIpfs(content).then((hash) => {
console.log("Hash of uploaded data: " + hash)
});
}}
style = {{height: 50}}>Upload Data to IPFS</button>
</div>
);
}
}
export default App;
Do I need to add an async function or something, I'm fairly new to js so any help would greatly appreciated. I just don't know how to change the ipfs.add to make my code work.
I have also followed the same tutorial and ran into the same problem. The ipfs.add function does not accept a call function anymore. More information on that here: https://blog.ipfs.io/2020-02-01-async-await-refactor/
The solution is turn your saveTestBlobOnIpfs function into an async/await function. Like this:
saveTestBlobOnIpfs = async (event) => {
event.preventDefault();
console.log('The file will be Submitted!');
let data = this.state.buffer;
console.log('Submit this: ', data);
if (data){
try{
const postResponse = await ipfs.add(data)
console.log("postResponse", postResponse);
} catch(e){
console.log("Error: ", e)
}
} else{
alert("No files submitted. Please try again.");
console.log('ERROR: No data to submit');
}
}