File upload using formik and express backend - express

I am trying to upload an image along with a title and some text for a post. I am using Formik form with an onSubmit in the client folder of the project :
const [charCount, setCharCount] = useState(0);
const [fileObject, setFileObject] = useState(null);
const initialValues = {
title: "",
postText: "",
file: null
};
const validationSchema = Yup.object({
title: Yup.string().required("Required"),
postText: Yup.string().required("Required"),
});
const onSubmitHandler = (values) => {
const formData = new FormData();
formData.append("title", values.title);
formData.append("postText", values.postText);
console.log(fileObject);
formData.append("file", document.getElementById("file").files[0]);
console.log(formData);
axios
.post(
"http://localhost:3001/posts/create",
formData,
{
headers: {
accessToken: localStorage.getItem("token"),
},
}
)
.then((response) => {
console.log(response.data);
});
};
return (
<>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmitHandler}
>
<Form className="create-post-form">
<input type="file" name="file" id="file" onChange={(e) => {
setFileObject(e.target.files[0]);
}}/>
<Field id="input-create-post" name="title" placeholder="Post Title" />
<ErrorMessage name="title" component="span" className="error" />
<Field
id="input-create-post-text"
name="postText"
placeholder="What's the story?"
as="textarea"
onKeyUp={(e) => setCharCount(e.target.value.length)}
maxLength="250"
/>
<div className="char-count">
<span id={charCount===250 ? "char-count-max" : "char-count"}>{charCount}/250</span>
</div>
<ErrorMessage name="title" component="span" className="error" />
<Button type="submit" title="Post" onSubmit={onSubmitHandler} />
</Form>
</Formik>
</>
);
};
On the server side (server folder), in the Posts.js route where I am handling the request :
router.post("/create",validateToken, async(req,res)=>{
if(req.files === null){
return res.status(400).json({message: "No file uploaded"});
}
const file = req.files;
console.log(file);
res.json(req.body)
})
On submitting the form, an empty object {} is logged on the console. And the res.json(req.body) returns the title and postText successfully, but the file is {} (empty). How do I fix this? Please help!
I tried submitting the form with the above code and got this response:
result image
I need the file object to be the actual fileObject that I have as a state.

Related

NextJS Mailchimp API Error Handling and useState

thanks a lot for taking a look at my problem.
I've been building a button to handle a Mailchimp API call but I can't seem to be able to piece it all together using useState. I would really appreciate some help with this as it is currently throwing 500 errors when successfully added to the mailing list with new email addresses and the response from Mailchimp seems to be fine.
I know there is probably some sort of react hook or other shortcut I could use but I really want to make this work..! hahaa!
api/subscribe.js
export default function handler(req, res) {
const { email } = req.body;
console.log(`email: ${email}`);
if (!email) {
return res.status(400).json({ error: 'Email is required' });
}
try {
const client = require('#mailchimp/mailchimp_marketing');
const API_KEY = process.env.MAILCHIMP_API_KEY;
const API_SERVER = process.env.MAILCHIMP_API_SERVER;
const LIST_ID = process.env.MAILCHIMP_AUDIENCE_ID;
client.setConfig({
apiKey: API_KEY,
server: API_SERVER,
});
const run = async () => {
const response = await client.lists.addListMember(LIST_ID, {
email_address: `${email}`,
status: 'pending',
skip_merge_validation: true,
});
console.log(response);
};
run();
if (response.status >= 400) {
return res.status(400).json({
error: `There was an error subscribing your email address to the newsletter.
Please contact us directly so we can add you in manually. Sorry for any inconvenience.`,
});
}
return res.status(201).json({
message: 'Success! 🎉 You are now subscribed to the newsletter.',
});
} catch (err) {
console.log(res.error);
return res.status(500).json({ error: '***ERROR***' });
}
}
My messy but functional code..!
components/Contact.js
import React, { useState } from 'react';
import LoadingSpinner from '../components/LoadingSpinner';
import { CountdownCircleTimer } from 'react-countdown-circle-timer';
export default function Contact() {
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
// const [loading, setLoading] = useState(true);
const [state, setState] = useState('IDLE');
const subscribe = async (e) => {
e.preventDefault();
setState('LOADING');
setMessage(null);
try {
const res = await fetch('/api/subscribe', {
body: JSON.stringify({
email: email,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
});
console.log(res);
if (res.status >= 400) {
console.log(res.error);
setState('ERROR');
setMessage(
<div>
An error has occurred, please{' '}
<a
href="/contact"
target="_blank"
rel="noreferrer"
className="text-blue-500 underline cursor-pointer transition-transform duration-7000 hover:text-gray-200"
onClick={() => {
setState('IDLE');
setMessage(null);
setEmail('');
}}
>
contact us
</a>
</div>
);
setEmail('');
setTimeout(() => {
setState('IDLE');
setMessage(null);
}, 10000);
return;
}
setState('SUCCESS');
setMessage('Success! 🎉 You are now subscribed to the newsletter.');
setEmail('');
setTimeout(() => {
setState('IDLE');
setMessage(null);
}, 10000);
return;
} catch (e) {
setState('ERROR');
console.log(e.res.error);
setMessage(e.res.error);
setEmail('');
setTimeout(() => {
setState('IDLE');
setMessage(null);
}, 10000);
return;
}
};
return (
<div className="h-[15rem] xs:h-[13.5rem] xsm:h-[12.5rem] sm:h-[13rem] w-full bg-black/90 px-2">
<div className="flex justify-center items-center">
<h1 className="text-center text-zinc-300 text-lg md:text-2xl mt-4">
Sign up to the Newsletter...
</h1>
</div>
<div className="flex justify-center items-center w-full">
<form onSubmit={subscribe}>
<input
type="email"
id="email-input"
name="email"
placeholder="Your#email.here"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
autoCapitalize="off"
autoCorrect="off"
aria-autocomplete="list"
className="h-[40px] min-w-[270px] md:min-w-[400px] border border-black/60 rounded-xl w-full text-center text-md px-2"
/>
<div className="flex justify-center items-center py-2">
{state === 'IDLE' && (
<button
type="submit"
value=""
name="subscribe"
disabled={state === 'LOADING'}
className="w-[200px] h-[40px] px-12 mt-2 text-lg border border-black/60 rounded-xl bg-gray-200 text-gray-900 transition-colors duration-700 transform hover:bg-gray-500 hover:text-gray-100"
>
Subscribe
</button>
)}
{state === 'ERROR' && (
<div className="h-[50px]">
<CountdownCircleTimer
isPlaying
size={55}
strokeWidth={2}
duration={10}
trailColor="#3399ff"
rotation="clockwise"
colors="#ffffff"
>
{({ remainingTime }) => remainingTime}
</CountdownCircleTimer>
</div>
)}
{state === 'SUCCESS' && (
<div className="h-[50px]">
<CountdownCircleTimer
isPlaying
size={50}
duration={10}
colors={[
['#004777', 0.33],
['#F7B801', 0.33],
['#A30000', 0.33],
]}
>
{({ remainingTime }) => remainingTime}
</CountdownCircleTimer>
</div>
)}
{state === 'LOADING' && (
<button
type="submit"
value=""
disabled
name="subscribe"
className="w-[200px] h-[40px] px-12 mt-2 text-lg border border-black/60 rounded-xl bg-gray-200 text-gray-900 transition-colors duration-700 transform"
>
<div className="flex justify-center items-center">
<LoadingSpinner />
</div>
</button>
)}
</div>
<div className="text-zinc-300 text-sm md:text-lg text-center md:min-w-[35ch]">
{message
? message
: `We only send emails when we have genuine news.`}
</div>
</form>
</div>
</div>
);
}
response from Mailchimp (with sensitive info taken out)
email: becir28216#jernang.com
undefined
{
id: '',
email_address: 'becir28216#jernang.com',
unique_email_id: '',
contact_id: '',
full_name: '',
web_id: ,
email_type: 'html',
status: 'pending',
consents_to_one_to_one_messaging: true,
merge_fields: { FNAME: '', LNAME: '' },
stats: { avg_open_rate: 0, avg_click_rate: 0 },
ip_signup: '',
timestamp_signup: '2022-11-15T20:46:33+00:00',
ip_opt: '',
timestamp_opt: '',
member_rating: 2,
last_changed: '2022-11-15T20:46:33+00:00',
language: '',
vip: false,
email_client: '',
location: {
latitude: 0,
longitude: 0,
gmtoff: 0,
dstoff: 0,
country_code: '',
timezone: '',
region: ''
},
source: 'API - Generic',
tags_count: 0,
tags: [],
list_id: '',
...}
I want to change the state of the button according to the response from the API call. I have been trying all sorts of combinations and now my head hurts..
Expected behaviour:
When it is idle, button says Subscribe and the normal message is displayed below it.
when it is loading, the button displays the loading spinner and still displays the normal message
When there is an error, the button displays the countdowntimer and the error message.
When the API call is a success, the loading spinner disappears, the success message is displayed and then 10 seconds later, the whole form resets again.

Resetting a formik form in react-native on navigation

I am trying to create a React Native app that can create and edit entities.
For example, users.
I have a formik form that takes first and last name, as well as an email address.
If a user params is passed, then the form should take on those values.
However, it only works the first time. After that, the form keeps the first values.
How can I force the form to be reevaluated?
function FormScreen({ navigation, route }) {
const formikRef = React.createRef();
const initialValues = { firstName: "", lastName: "", email: "" };
if (route.params && route.params.user) {
if (route.params.user.firstName) {
initialValues.firstName = route.params.user.firstName;
}
if (route.params.user.lastName) {
initialValues.lastName = route.params.user.lastName;
}
if (route.params.user.email) {
initialValues.email = route.params.user.email;
}
}
const unsubscribeBlur = navigation.addListener("blur", (e) => {
console.log("form blur");
if (formikRef.current) {
console.log("form reset");
formikRef.current?.resetForm();
}
});
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Formik
innerRef={formikRef}
initialValues={initialValues}
validationSchema={Yup.object({
firstName: Yup.string()
.max(15, "Must be 15 characters or less")
.required("Required"),
lastName: Yup.string()
.max(20, "Must be 20 characters or less")
.required("Required"),
email: Yup.string()
.email("Invalid email address")
.required("Required"),
})}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
<Form>
<label htmlFor="firstName">First Name</label>
<Field name="firstName" type="text" />
<ErrorMessage name="firstName" />
<label htmlFor="lastName">Last Name</label>
<Field name="lastName" type="text" />
<ErrorMessage name="lastName" />
<label htmlFor="email">Email Address</label>
<Field name="email" type="email" />
<ErrorMessage name="email" />
<button type="submit">Submit</button>
</Form>
</Formik>
</View>
);
}
export default FormScreen;
I have code in there to reset the form on blur but the reset doesn't seem to do anything.
You can find the complete code in a snack.
https://snack.expo.io/#hackzilla/create-and-edit-with-formik
Because you are using a tab navigator. The home and form screen will not reMount when you toggle between them.So the formik will not load the new initialValues.In your way,you can reset the form using route params everytime the form screen is focused.But a better way is using a stack navigator,every time create a form using a new instance.
useFocusEffect(
React.useCallback(() => {
if (formikRef.current) {
console.log("form reset");
formikRef.current?.setValues(initialValues);
}
})
);
We can reset the form after submit, so that it will clear all the fields. Please try by replacing your submit method with below mentioned code.
onSubmit={(values, { setSubmitting, resetForm }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
// reset the fields after submit
resetForm();
}, 400);
}}

profilePic.move is not a function [AdonisJS]

I am facing the error while trying to upload a file(image) via the form on AdonisJS (I referred to the official docs #AdonisJS4.1 File Uploads)
await profilePic.move(Helpers.tmpPath('uploads'), {
name: 'custom.jpg',
overwrite: true
})
if (!profilePic.moved()) {
console.log('file not moved')
}
Official Docs Here
HTML
<form method="POST" action="upload" enctype="multipart/form-data">
<input type="file" name="profile_pic" />
<button type="submit"> Submit </button>
</form>
JS
const Helpers = use('Helpers')
Route.post('upload', async ({ request }) => {
const profilePic = request.file('profile_pic', {
types: ['image'],
size: '2mb'
})
await profilePic.move(Helpers.tmpPath('uploads'), {
name: 'custom-name.jpg',
overwrite: true
})
if (!profilePic.moved()) {
return profilePic.error()
}
return 'File moved'
})

Editing a Material UI row within React + Redux

I have a material UI table in a react project, and I want a user to click on the pencil/Edit Icon to edit the table row.
This is a loaded question, but would this require an additional piece of Material UI? what would the logic look like to make this happen?? I have most of the code set up on the backend, but don't know how to write this code in the component???
Thanks for looking!
//STYLE VARIABLE BOR MATERIAL BUTTON
const style = {
margin: 12
//
};
const mapStateToProps = (state) => ({
user: state.user,
reduxState: state.getExpense
});
class ExpenseTable extends Component {
constructor(props) {
super(props);
this.state = {
getExpense: []
};
}
isSelected = (index) => {
return this.state.selected.indexOf(index) !== -1;
};
handleRowSelection = (selectedRows) => {
this.setState({
selected: selectedRows,
});
};
//on page load, DISPATCH GET_EXPENSE is
//SENT TO expenseSaga which then
//goes to getExpenseReducer and appends EXPENSE_DATA to the
//DOM
componentDidMount() {
const { id } = this.props.match.params;
this.props.dispatch({ type: USER_ACTIONS.FETCH_USER });
this.props.dispatch({ type: 'GET_EXPENSE' });
}
componentDidUpdate() {
if (!this.props.user.isLoading && this.props.user.userName ===
null) {
this.props.history.push('home');
}
}
logout = () => {
this.props.dispatch(triggerLogout());
// this.props.history.push('home');
};
//SETS STATE FOR ALL INPUTS
handleChange = (name) => {
return (event) => {
this.setState({
[name]: event.target.value
});
};
};
//SUBMIT BUTTON- TRIGGERS DISPATCH TO EXPENSE SAGA TO ADD DATA
handleClick = () => {
console.log('add expense', this.state);
this.props.dispatch({
type: 'ADD_EXPENSE',
payload: this.state
});
};
//TRASH ICON-TRIGGERS DISPATCH TO EXPENSE SAGA DELETE
handleClickRemove = (id) => {
console.log('delete expense', this.state);
this.props.dispatch({
type: 'DELETE_EXPENSE',
payload: id
});
};
handleClickEdit = (row) => {
console.log('edit expense', this.state)
this.props.dispatch({
type: 'EDIT_EXPENSE',
payload: row
})
}
render() {
console.log('HEY-oooo expense render', this.state);
let content = null;
if (this.props.user.userName) {
//MAP OVER REDUX STATE.
const tableRows = this.props.reduxState.map((row) => {
//.MAP SEPARATES DATA INTO INDIVIDUAL ITEMS.
const { id, item_description, purchase_date,
item_price, item_link } = row;
return (
<TableRow selectable={false}>
{/* TABLE ROWS */}
<TableRowColumn>{item_description}
</TableRowColumn>
<TableRowColumn>{purchase_date}
</TableRowColumn>
<TableRowColumn>${item_price}</TableRowColumn>
<TableRowColumn><a href={item_link}>{item_link}
</a></TableRowColumn>
<TableRowColumn><EditIcon class="grow:hover"
onClick={() => {this.handleClickEdit(row)}} />
</TableRowColumn>
<TableRowColumn><TrashIcon onClick={() =>
{this.handleClickRemove(id);
}}/>
</TableRowColumn>
</TableRow>
// END TABLE ROWS
);
});
<div>
{/* FORM FOR ADDING EXPENSES(DATA) */}
<form id="expenseForm">
<h3>
Add a new <br />
expense
</h3>
<input
type="text"
id="fname"
name="fname"
placeholder="Item description"
onChange=
{this.handleChange('item_description')}
/>
<br />
<br />
<input
type="text"
id="lname"
name="lname"
placeholder="Item price"
onChange={this.handleChange('item_price')}
/>
<br />
<input
type="text"
id="lname"
name="lname"
placeholder="Item link"
onChange={this.handleChange('item_link')}
/>
<br />
{/* END FORM */}
<RaisedButton
id="expSubmit"
label="Submit Expense"
primary={true}
style={style}
onClick={this.handleClick}
/>
{/* TABLE TOTAL KEEPS CURRENT TOTAL OF PRICE COLOUMN */}
<h1>Total:</h1>
<br />
<h3>$748.93</h3>
</form>
{/* TABLE HEADERS */}
<Table>
<TableHeader>
<TableRow>
<TableHeaderColumn>Item
description</TableHeaderColumn>
<TableHeaderColumn>Purchase
Date</TableHeaderColumn>
<TableHeaderColumn>Item
Price</TableHeaderColumn>
<TableHeaderColumn>Item
Link</TableHeaderColumn>
<TableHeaderColumn>Edit
entry</TableHeaderColumn>
<TableHeaderColumn>Delete
entry</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody>
{tableRows}
</TableBody>
</Table>
</div>
);
}
return (
<div>
<Nav />
{content}
</div>
);
}
}
// this allows us to use <App /> in index.js
export default connect(mapStateToProps)(ExpenseTable);
The default material-ui table doesn't have the feature of in-line editing. You will have to write your own wrapper to achieve this which can be time consuming. To overcome the time issue we can use some good existing libraries.
Here's one powerful library from DevExtreme for the same which i used, but go through their licensing before you take the final decision to use.
https://devexpress.github.io/devextreme-reactive/react/grid/demos/featured/controlled-mode/

How can I upload image to server using axios

I am trying to send a form along with an image. Note: I don't want to save the image in a database, I want to save it in a folder which I created on the server and just add a link of the image to the database.
From the server side I know how to handle this, but I don't know how to do this from font-end. With other words, how can I send the image using axios to the server.
<template>
<input type="text" class="form-control" id="specdesc" v-model="product.specdesc" name="specdesc" placeholder="Enter your name">
<input type="file" name="image" id="image" accept="image/*" >
<button type="submit" class="btn btn-sm btn-primary"#click.prevent="Submit()"> Submit</button>
</template>
<script>
export default {
name: 'Addproduct',
data(){
return{
image: '',
product:{
specdesc:'',
},
}
},
methods:{
Submit(){
var _this=this
// console.log(this.image)
console.log(this.selectedCategory.category_name)
var product = {
specification:this.product.specdesc,
imagename:this.image
}
this.$http.post('http://localhost:3000/api/companyproducts',product)
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log("error.response");
});
}
},
}
</script>
Now my question is how can I upload a image as well as the input name using axios. Moreover I want to use the same method i.e var product to send.
A standard (mostly) approach will be to split the logic in two, if you want to save the image path on your product, you firstly need to upload the photo to the server and return their path.
pseudo example:
component's data
{
imagePath: '',
productSpect: ''
}
``
``html
<input type="text" v-model="productSpect" />
<input type="file" #change="uploadImage" name="image" id="image" accept="image/*" >
<button type="submit" #click.prevent="submit"> Submit</button>`
``
**uploadImage method**
uploadImage (e) {
let img = e.target.files[0]
let fd= new FormData()
fd.append('image', img)
axios.post('/upload-image', fd)
.then(resp => {
this.imagePath = resp.data.path
})
}
**submit method**
submit () {
let data = {
imagePath: this.imagePath,
productSpect: this.productSpect
}
axios.post('/path/to/save', data)
}
**edited method to handle just only 1 image on the server**
Change the input `#change` to just save the img on a property under data():
<input type="file" #change="image = e.target.file[0]" name="image" id="image" accept="image/*" >
submit() {
let fd= new FormData()
fd.append('image', this.image)
axios.post('/upload-image', fd)
.then(resp => {
this.imagePath = resp.data.path
let data = {
imagePath: this.imagePath,
productSpect: this.productSpect
}
axios.post('/path/to/save', data)
})
}
There is nothing specific to Vue in this question. To send a POST request with axios, the easiest thing to do is to grab the formData of the html form and pass it as the data argument to Axios. To do this in Vue, just give your form tag a ref, then create a formData from the form.
<form ref="myForm">
// then in your method...
var myFormData = new FormData(this.$refs.myForm)
axios({
method: 'post',
url: 'myurl',
data: myFormData,
config: { headers: {'Content-Type': 'multipart/form-data' }}
})
for upload file I've used vuetify https://vuetifyjs.com/en/components/file-inputs/
<v-file-input
accept="image/png, image/jpeg, image/bmp"
placeholder="Pick an avatar"
prepend-icon="mdi-camera"
v-model="imageData"
></v-file-input>
<v-btn color="primary" #click="onUpload">Upload</v-btn>
in my vue method I have
onUpload() {
let formData = new FormData();
formData.append('file', this.imageData);
axios.post(
"/images/v1/upload"
,formData
,{headers: {"Content-Type": "multipart/form-data"}}
)
.then(response => {
//...
})
.catch(e => {
//...
})
}
Best regards!