Sending photo with vuejs - javascript

I am using ElementUi uploader and i need to send my file with the rest of my form data, but it doesn't seem to send right details of photo to back-end:
Screenshots
Console log when i select an image
Data that sent to back-end
Code
photo input
<el-upload
action="#"
:limit="1"
:multiple="false"
:on-change="photoChanged"
:on-exceed="handleExceed"
list-type="picture-card"
:on-remove="handleRemove"
:on-preview="handlePictureCardPreview"
:before-remove="beforeRemove"
:auto-upload="false">
<i slot="default" class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
Script
export default {
data() {
return {
dialogImageUrl: '',
dialogVisible: false,
form: {
name: '',
slug: '',
price: '',
new_price: '',
sku: '',
qty: 1,
active: '',
photo: '',
shortDesc: '',
longDesc: '',
region: '',
date1: '',
date2: '',
type: [],
tags: [],
brand_id: '',
categories: [],
resource: '',
user_id: ''
}
}
},
methods: {
onSubmit(e) { //send data to back-end
e.preventDefault();
axios.post('/api/admin/products/store', this.form)
.then(res => {
console.log(res);
})
.catch(error => {
console.log(error);
})
},
handleRemove(file) {
this.form.photo = ''; // remove photo from from when it's removed
},
photoChanged(file, fileList){
this.form.photo = file.raw; // add photo to form when it's selected
console.log('file', file) // screenshot 1
console.log('raw', file.raw) //screenshot 2
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
handleExceed(files, fileList) {
this.$message.warning(`The limit is 1, you selected ${files.length} files this time, add up to ${files.length + fileList.length} totally, remove old image and try again.`);
},
beforeRemove(file) {
return this.$confirm(`Cancel the transfert of ${ file.name } ?`);
}
},
}
</script>
Any idea?

I have used FormData to send the photo or document to the server.
JavaScript FormData
<form id="myForm" name="myForm">
<div>
<label for="username">Enter name:</label>
<input type="text" id="username" name="username" v-model="imageData.name">
</div>
<div>
<label for="useracc">Enter account number:</label>
<input type="text" id="useracc" name="useracc" v-model="imageData.account">
</div>
<label for="userfile">Upload file:</label>
<input type="file" id="userfile" name="userfile">
</div>
<input type="submit" value="Submit!">
</form>
export default {
data() {
return {
imageData: {}
}
},
methods: {
uploadImageToServer() {
// 1.Save the form Data and return the new id to save the image
axios.post('/api/admin/products/store', this.imageData)
.then(res => {
if(res.id) {
//2. Save the image to id
let formData = new FormData();
let file = document.getElementById('userfile');
formData.append('file', file)
axios.post('/api/admin/products/image/{id}', formData)
.then(res => {
console.log(res)
})
}
})
.catch(err => {
console.log(err)
})
}
}
}
Here,
Both form data & file data maynot be send in single requst.
1. Saving the form data and return the id.
2. Saving the image data to the id.
Replace the html with 'element-ui' syntax. Ensure that your rest api receives the form data as the input.

convert your file to base64
when you select an image, use code below
onImageChange() {
let file = this.form.photo
if (file == '')
return;
this.createImage(file);
}
createImage(file) {
let reader = new FileReader();
let el = this
reader.onload = (e) => {
el.form.photo = e.target.files[0];
};
reader.readAsDataURL(file);
},
attach onImageChange function in your input file

Solved
Well I have decided to give up on sending image with rest of data to backend and upload image first with action="#" in my input and in return i get file name in my form and just send the file name with rest of form instead of sending image file.
<el-upload
action="/api/upload"
:on-success="handleAvatarSuccess"
.....>
methods: {
handleAvatarSuccess(res, file) {
this.form.photo = res.data;
},
}
So it sends my file to back-end as soon as it's selected and set the name of stored file in my form.photo and that name will be send with rest of my form inputs.
Hope it could be useful to others as well.

Related

No file selected error not showing in vue

I have a defined a file upload field but the issue here is I am able to submit the form even if I have not selected a file. Please help me figure out how to through an error that no file is selected on clicking on the submit button if no file is selected. I am using vuetify version 1.0.
<template>
<v-form :model='agency' ref='AgencyForm'>
<div class="vue-file-wrapper">
<input
type="file"
ref="file"
name="vue-file-input"
required
:rules='uploadDocument'
#change="onFileSelected"
>
</div>
<v-btn #click.prevent='submit'>Save</v-btn>
</v-form>
</template>
<script>
export default {
props: ['agency'],
data: function () {
return {
filename: '',
uploadDocument: [
value => !!value || 'Please upload document'
],
}
}
methods: {
onFileSelected(event) {
var files = event.target.files || event.dataTransfer.files;
if (!files.length) {
return;
}
this.createImage(files[0]);
},
createImage(file) {
var fileReader = new FileReader(),
that = this;
fileReader.onload = function(event) {
that.agency.document = event.target.result;
that.agency.filename = file.name;
that.filename = file.name;
};
fileReader.readAsDataURL(file);
},
submit() {
if (this.$refs.AgencyForm.validate()) {
this.$axios.put('/agency.json', { agency: this.agency })
}
</script>
I can see some issues with your current implementation. Firstly, you are directly mutating a prop agency, which isn't a good practice. Also, you aren't waiting for your axios request to complete on submission.
However, for your current situation of not having an error thrown when no file is selected on clicking on the submit button, I think the issue is a syntax problem.
You currently have
<div class="vue-file-wrapper">
<input
type="file"
ref="file"
name="vue-file-input"
required
:rules='uploadDocument'
#change="onFileSelected"
>
According to the documentation, it should be
<v-file-input
:rules="uploadDocument"
#change="onFileSelected"
>
</v-file-input>
You can then leave the data property as it was
data: function () {
return {
filename: '',
uploadDocument: [
value => !!value || 'Please upload document'
],
}
}
EDIT -- since in Vuetify 1.0.5, there's no support for v-file-input, from this Github issue, you can do this
<template>
<div>
<v-text-field prepend-icon="attach_file" single-line
v-model="filename" :label="label" :required="required"
#click.native="onFocus"
:disabled="disabled" ref="fileTextField"></v-text-field>
<input type="file" :accept="accept" :multiple="false"
ref="file" #change="onFileSelected">
</div>
</template>
Your data property now becomes
data: function () {
return {
filename: '',
uploadDocument: [
value => !!value || 'Please upload document'
],
errors: {
file: ''
}
}
}
You can then style the text field using SCSS/CSS to be below the file input field or something.
One thing is for sure, the rules prop will not work on a input element because it's reserved for vuetify specific elements.
It won't be triggered by this.$refs.AgencyForm.validate() for that very reason. You will have to write custom validation
Maybe something along the lines of
methods: {
validateFile(file) {
if (!file.name) {
errors.file = 'Please select a file';
} else {
errors.file = '';
}
}
atLeastOneErrorExists(errors) {
return Object.values(errors).some(error => error.length > 0)
}
onFileSelected(event) {
var files = event.target.files || event.dataTransfer.files;
if (!files.length) {
return;
}
var file = files[0];
this.filename = file.name;
this.createImage(files[0]);
},
submit() {
this.validateFile(this.filename);
if (this.atLeastOneErrorExists(this.errors)) {
this.$axios.put('/agency.json', { agency: this.agency })
}
}
}
In your template, you can simulate the error message of Vuetify by styling a p tag that looks similar to Vuetify error messages
Something like
<div class="vue-file-wrapper">
...
<input
...
>
<p class="custom-error-class">{{errors.file}}</p> // custom error message
</div>

VueJS Laravel - Uploading image using Axios post error: 'The given data was invalid'

For school I'm making a blog with some people and now I want to upload an image to the blog post. But then this uploading the post the error:
{message: "The given data was invalid.", errors: {image: ["The image field is required."]}}
errors: {image: ["The image field is required."]}
message: "The given data was invalid."
I have searched on the internet and looked at other conversations, but I can't find my solution.
Here are my codes:
This is the form in a VueJS component:
<form method="post" action="./api/post" #submit.prevent="createPost()" enctype="multipart/form-data">
<p>Titel van de post</p>
<input type="text" placeholder="Titel van de post" name="title" v-model="title">
<p>Beschrijving bij de post</p>
<textarea placeholder="Beschrijving bij de post" name="description" v-model="description"/>
<input type="file" id="image" name="image" ref="image" v-on:change="(e) => {this.onChangeFileUpload(e)}"/>
<div class="uploadtimer">
<p>Selecteer een tijd wanneer de post moet worden getoond. (Niet verplicht)</p>
<datetime format="YYYY-MM-DD H:i" v-model="uploadTime"></datetime>
</div>
<input type="submit" class="input-submit">
</form>
The script in the component:
<script>
import datetime from 'vuejs-datetimepicker';
export default {
components: { datetime },
data() {
return {
title: '',
description: '',
uploadTime: '',
image: '',
}
},
methods: {
onChangeFileUpload(e) {
this.image = e.target.files[0];
},
createPost() {
let data = new FormData;
data.append('image', this.image);
axios.post('./api/post', {
title: this.title,
description: this.description,
data,
uploadTime: this.uploadTime,
}).then(response => {
}).catch(response => {
document.getElementById('errorMSG').style.display = 'flex';
document.getElementById('errorMSG').innerHTML = '<span>!</span><p>Er is iets mis gegaan met het plaatsen van de post.</p>';
setTimeout(function() {
document.getElementById('errorMSG').style.display = 'none';
}, 5000);
});
}
},
}
And the Controller function I'm using:
public function store(Request $request)
{
if(Auth::check()) {
$this->validate($request, [
'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg',
]);
$post = new Post();
$post->title = $request->input('title');
$post->description = $request->input('description');
$image = $request->file('image');
$imageExtension = $image->getClientOriginalExtension();
$imageName = time() . '_' . $imageExtension;
$image->move(public_path('/imgs/users-avatars/'), $imageName);
$post->image = '/imgs/posts-images/' . $imageName;
$post->uploadTime = $request->input('uploadTime');
$post->user_id = Auth::User()->id;
$post->save();
return (['message' => 'succes']);
} else {
return (['message' => 'error', 'handler' => 'Authenticated user is required!']);
}
}
How can I fix this?
You may use either image rule (that validates a file to be jpeg, png, bmp, gif, svg, or webp) or use mimes:jpeg,png,jpg,gif,svg:
So your validation would be either:
'image' => 'required|file|mimes:jpeg,png,jpg,gif,svg|max:2048',
Or:
'image' => 'required|file|image|max:2048',
Additionally, the file rule checks if the field is a successfully uploaded file and max:2048 rule checks if the file size is less than or equal to 2048 kilobytes (2MB). See Laravel docs for more info.
On VueJs, you have to append all your inputs to a FormData:
let data = new FormData;
data.append('image', this.image);
data.append('title', this.title);
data.append('description', this.description);
data.append('uploadTime', this.uploadTime);
axios.post('./api/post', data)
.then(
//...

Image not Uploading in Vue Js Laravel

I have a problem in my image upload in Laravel Vue. I do not upload image in my project.Here is My Code
<form action="">
<input type="text" id="firstName" class="form-control" v-model="user.firstName" value="Frankie">
<input type="text" id="lastName" class="form-control" v-model="user.lastName" value="Apple">
<select v-model="user.profile.country" v-chosen="user.profile.country" class="option-select">
<option v-for="(country, key) in countries" :value="key">{{country}}</option>
</select>
<input type="file" ref="files" id="imgInp" multiple #change="selectFile">
<input type="button" class="button button__primary button__agree" value="Confirm" #click="submit">
</form>
<script>
export default {
data() {
return {
user : [],
files : [],
uploadFiles : [],
}
},
methods : {
selectFile() {
const files = this.$refs.files.files;
this.uploadFiles = [ ...this.uploadFiles, ...files];
this.files = [
...this.files,
..._.map(files, file => ({
name: file.name,
type: file.type,
size: file.size,
}))
];
},
submit() {
var data = {
'customerDetail': {
'firstName' : this.user.firstName,
'lastName' : this.user.lastName,
'address' : {
'country' : this.user.profile.country,
},
'application': {
'attachments' : this.uploadFiles,
},
},
};
const config = {
headers: { 'content-type': 'multipart/form-data' }
};
axios
.post(`/web/bookings`, data, config)
.then((e) => {
console.log(e);
})
.catch((e) => {
console.log(e);
})
},
},
}
</script>
But When I submit Data it shows error
Missing boundary in multipart/form-data POST data
If I remove config data then my image is not uploaded. I don't get where the problen is. Please help me to solve this problem. Thanks in advance

Get value from emit in input field with Vue

I know this has a simple answer but I appear to be stuck. I have an upload image input in a form. Following several tutorials, I have successfully created the upload method. My issue is once the image is uploaded to Firestore storage I use this.$emit('imgurl', downloadURL)
My problem is I do not know how to get that value so when the user submits the form the url gets added to the database.
Parts of the code:
HTML:
<div class="field avatar">
<label for="avatar">Avatar</label>
<input type="file" name="imgurl" accept="image/*" #change="detectFiles($event.target.files)">
<div class="progress-bar green" :style="{ width: progressUpload + '%'}">{{ progressUpload }}%</div>
<img class="avatar" v-bind:src="this.downloadURL">
</div>
Methods:
detectFiles (fileList) {
Array.from(Array(fileList.length).keys()).map( x => {
this.upload(fileList[x])
})
},
upload (file) {
var storage = firebase.storage();
this.uploadTask = storage.ref('avatars/'+file.name).put(file);
}
Watch:
watch: {
uploadTask: function() {
this.uploadTask.on('state_changed', sp => {
this.progressUpload = Math.floor(sp.bytesTransferred / sp.totalBytes * 100)
},
null,
() => {
this.uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
this.downloadURL = downloadURL
this.$emit('imgurl', downloadURL)
})
})
}
}
Add to the database:
db.collection('teams').add({
team_name: this.team_name,
team_id: this.team_id,
bio: this.bio,
url: this.imgurl,
}).then(() => {
this.$router.push({ name: 'Admin' })
}).catch(err => {
console.log(err)
})
You can pass a function as a prop to a child component, then call this function passing your downloadURL as argument.
Parent Component
HTML
<child passURL="getDownloadURL">
JS
data: {
return {
downloadURL: null
}
},
methods: {
getDownloadURL: function(url) {
this.downloadURL = url
}
}
Child Component
JS
props: ['passURL'],
Inside your watcher, you can call
this.passURL(downloadURL)
Instead of $emit.
I found the answer. I added a hidden input field
<input type="hidden" name="imgurl" v-model="imgurl">
and replaced the emit with this.imgurl = downloadURL

Reactjs and Stripe: show success and error messages on submit

I am working on a payment system with Stripe on Reactjs.
I want to be able to display Success or Error messages but I am still new to React and I'm not sure where the code should be placed.
Success message: for when the payment is successful.
Error message: if there was some problem with the payment.
I also want to show the activation code they receive as a response, once the token is created. Like this:
{this.state.code && Your activation code (code)is: {this.state.code} and it is valid for {this.state.subscription_days} days.
But it does not work.
class CheckoutForm extends Component {
constructor(props) {
super(props);
this.state = {
complete: false,
referrer: '',
email: '',
amount: '',
};
this.submit = this.submit.bind(this);
}
componentDidMount() {
console.log(this.props);
let referrer = this.props.match.params.referrer; // url - added route to index.js
if (referrer) {
console.log(referrer);
this.setState({ referrer, });
}
// let amount = this.state.amount;
// document.getElementById('get3months').addEventListener('click', amount._handleAmount);
}
// user clicked submit
submit(ev) {
ev.preventDefault(); // prevent default submission and refreshing the page
this.props.stripe.createToken() // which elements to tokenize
.then(response => {
console.log('Received Stripe token:', response.token);
axios.post('http://10.250.57.37:8000/subscriptions/codes/pay/',
{
token: response.token,
amount: this.state.amount,
email: this.state.email,
referrer: this.state.referrer, // rn name or empty string, filip
},
{
'Content-Type': 'application/json', // header
}
)
.then(response => {
console.log(response);
});
})
.catch(error => {
console.log(error);
});
}
render() {
if (this.state.complete) return <p>Purchase Complete!</p>;
return (
<div className="checkout-form">
<PrimeHeading
heading={this.props.heading}
subheading={this.props.subheading}
/>
<p>Would you like to complete the purchase?</p>
<form onSubmit={this.submit} style={{ minHeight: 300, }}>
<label>
Email
<input
id="email"
name="email"
type="email"
placeholder="Enter your email"
required
onChange={this._handleEmailChange.bind(this)}
value={this.state.email}
/>
</label>
{/* <label>
Referral
<input
id="referrer"
name="referrer"
type="text"
placeholder="Enter your friends' usernames"
required
/>
</label> */}
<CheckoutCardSection />
<Button
// label="Pay" {this.state.amount} "DKK"
onClick={this.submit}
type="button"
size="medium"
backgroundColor="#43ddb1"
color="white"
noShadow
/>
</form>
{this.state.code && <div>Your activation code is: {this.state.code} and it is valid for {this.state.subscription_days} days.</div>}
</div>
);
}
_handleEmailChange(event) {
let email = event.target.value;
this.setState({ email, });
}
}
Let me know if you need more explanation. Help is MUCH appreciated!
In the code that you are showing, you should set a new state in either the then or catch callbacks. You can have some extra properties in your component's state to achieve this.
...
this.state = {
complete: false,
referrer: '',
email: '',
amount: '',
code: null,
subscription_days: null,
error: null,
};
...
And then, you would set it like this:
...
.then(response => {
console.log('Received Stripe token:', response.token);
axios.post('http://10.250.57.37:8000/subscriptions/codes/pay/',
{
token: response.token,
amount: this.state.amount,
email: this.state.email,
referrer: this.state.referrer, // rn name or empty string, filip
},
{
'Content-Type': 'application/json', // header
}
)
// Use the appropiate property in your response to set the values.
// Note that I'm using destructuring assignment
.then(({ code, subscription_days })=> {
this.setState({
code,
subscription_days
});
});
})
.catch(error => {
this.setState({
error: `Your error message.`//Maybe error.message?
});
});
...
Finally, I'd recommend to pull out your network call code from the component to a separate module and just return the response. It'd make your component code more readable.

Categories