I'm trying to upload a pdf /doc file using VUE and laravel but after submitting, The file shows no value.
I have a VUE component with this form and this script:
export default {
data: function() {
return {
product_list: false,
add_form: true,
edit_form: false,
form: {
po_order_docs: '',
},
errors: {},
};
},
methods: {
processFile() {
this.file = _this.$refs.file.files[0];
let formData = new FormData();
formData.append('this.form.po_order_docs', this.file);
},
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<label class="col-lg-2 col-form-label">Order Docs</label>
<div class="col-lg-4">
<div class="input-group">
<input type="file" class="custom-file-input m-input" id="file" #change="processFile()" ref="file" />
<label class="custom-file-label" for="file">Choose file</label>
</div>
</div>
Related
I have a form that has an array input and an image, when sending the request without the image it's getting stored correctly in the database, when the image input and the "Content-Type": "multipart/form-data" header both added, it send the request with the correct data but it outputs that the array data is invalid.
the code is written in Vue 3 and the backend is a laravel api.
<script>
import axios from "axios";
export default {
name: "AddPayloadModel",
data() {
return {
drone_id: [],
drone_models: [],
image: null,
previewImage: null,
};
},
created() {
this.getDroneModels();
},
methods: {
getDroneModels() {
axios.get("http://127.0.0.1:8000/api/drone-models").then((response) => {
this.drone_models = response.data;
});
},
imgUpload(e) {
this.image = e.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(this.image);
reader.onload = (e) => {
this.previewImage = e.target.result;
};
},
submit(e) {
const form = new FormData(e.target);
const inputs = Object.fromEntries(form.entries());
inputs.drone_id = Object.values(this.drone_id);
console.log(inputs.drone_id);
axios
.post("http://127.0.0.1:8000/api/payload-model/add", inputs, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((res) => {
if (res.data.isUnattachableDrones) {
console.log(res);
alert(res.data.unattachableDrones);
} else {
this.$router.push("/home");
}
})
.catch((e) => {
console.error(e);
});
},
},
};
</script>
<template>
<main class="form-signin">
<form #submit.prevent="submit">
<h1 class="h3 mb-3 fw-normal">Add Payload Model</h1>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="brand_name"
placeholder="Brand Name"
/>
<label>Brand Name</label>
</div>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="model_name"
placeholder="Model name"
/>
<label>Model Name</label>
</div>
<div class="form-floating">
<input
class="form-control"
autocomplete="off"
name="type"
placeholder="Type"
/>
<label>Type</label>
</div>
<select
v-model="drone_id"
multiple="multiple"
name="drone_id"
style="height: auto"
class="form-select last-input"
>
<template v-for="drone in drone_models">
<option :value="drone.id">
{{ drone.brand_name }}
</option>
</template>
</select>
<input
name="image"
ref="fileInput"
accept="image/*"
type="file"
#input="imgUpload"
/>
<button class="w-100 btn btn-lg btn-form" type="submit">Submit</button>
</form>
</main>
</template>
here is the response when submitting the form.
and here is the payload.
It got solved by changing the way to push data to the post request to a formData and append it manually like this.
submit() {
const formData = new FormData();
formData.append(
"brand_name",
document.getElementById("brand_name").value
);
formData.append(
"model_name",
document.getElementById("model_name").value
);
formData.append("type", document.getElementById("type").value);
formData.append("image", document.getElementById("image").files[0]);
this.drone_id.forEach((drone_id) => {
formData.append("drone_id[]", drone_id);
});
const headers = { "Content-Type": "multipart/form-data" };
axios
.post("http://127.0.0.1:8000/api/payload-model/add", formData, headers)
.then((res) => {
console.log(res);
if (res.data.isUnattachableDrones) {
console.log(res);
alert(res.data.unattachableDrones);
} else {
this.$router.push("/home");
}
})
.catch((e) => {
console.error(e);
});
},
Simple form with the image upload and a title / description input
<template>
<div class="upload-form">
<form action="api/album" method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="title">Title</label>
<input
v-bind="form.title"
type="text"
class="form-control"
id="title"
required
/>
</div>
<div class="form-group">
<label for="description">description</label>
<input
v-bind="form.Description"
type="text"
class="form-control"
id="description"
required
/>
</div>
Upload a cover image here
<vue-dropzone
ref="dropzoneJs"
id="dropzone"
:options="options"
v-on:vdropzone-sending="sendingEvent"
></vue-dropzone>
<button
v-on:click="processQueue"
id="upload"
type="submit"
class="btn btn-primary"
>
Upload
</button>
</form>
</div>
</template>
<script>
import vueDropzone from "vue2-dropzone";
export default {
data() {
return {
options: {
url: "http://127.0.0.1:8000/api/album",
addRemoveLinks: true,
maxFiles: 1,
maxFilesize: 4,
autoProcessQueue: false,
dictDefaultMessage: '<i class="fas fa-cloud-upload-alt"></i>UPLOAD',
},
form: {
title: "",
Description: "",
},
};
},
components: {
vueDropzone: vueDropzone,
},
methods: {
processQueue() {
this.$refs.dropzoneJs.processQueue();
},
sendingEvent(file, xhr, formData) {
formData.append("title", this.form.title);
formData.append("description", this.form.Description);
},
},
};
</script>
<style scoped>
#upload {
margin-top: 5px;
}
.upload-form {
margin-top: 75px;
}
</style>
Form get's send to laravel backend controller via this route
Route::post('/album', [AlbumController::class, 'store'])->name('album.post');
Method inside of controller just dumps the request variable
public function store(Request $request)
{
dd($request);
}
The upload hits the POST endpoint and prints out the $request variable, but no title, description or image are inside.
After some trial and error I have found a solution
1: I removed the form tag
2: I used v-model instead of v-bind
3: changed the URL inside of my options object to api/album
these changes seem to have fixed my issue and dd($request) now prints the title / description and the file
I am making a simple website that has a feature to upload images. I tried it Laravel way which I made it in blade template and it works fine. Now I am trying to make it inside Vue Components
Here's my Create.vue
<template>
<div>
<div class="row">
<input type="hidden" name="_token" :value="csrf">
<div class="col-md-5">
<div class="detail-container">
<label for="title">Book Title:</label>
<input type="text" name="title" id="title" v-model="book_title" class="form-control">
</div>
<div class="detail-container">
<label for="title">Book Description:</label>
<textarea type="text" name="description" id="description" v-model="book_description" class="form-control" rows="5"></textarea>
</div>
<div class="detail-container">
<label for="title">Tags:</label>
<multiselect v-model="tags" :show-labels="false" name="selected_tags" :hide-selected="true" tag-placeholder="Add this as new tag" placeholder="Search or add a tag" label="name" track-by="id" :options="tagsObject" :multiple="true" :taggable="true" #tag="addTag" #input="selectTags">
<template slot="selection" slot-scope="tags"></template>
</multiselect>
</div>
</div>
<div class="col-md-7">
<!-- BOOK COVER WILL GO HERE -->
<div class="detail-container">
<label>Book Cover:</label>
<input type="file" class="form-control-file" id="book_cover" name="selected_cover" #change="onFileChange">
<small id="fileHelp" class="form-text text-muted">After you select your desired cover, it will show the preview of the photo below.</small>
<div id="preview">
<img v-if="url" :src="url" height="281" width="180" />
</div>
</div>
</div>
<div class="detail-container" style="margin-top: 20px;">
<button class="btn btn-primary" #click="saveBook()">Next</button>
</div>
</div>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
// register globally
Vue.component('multiselect', Multiselect)
export default {
// OR register locally
components: { Multiselect },
data () {
return {
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
url: null,
selected_cover: null,
tags: [],
tagsObject: [],
selected_tags: [],
book_title: '',
book_description: ''
}
},
methods: {
getTags() {
let vm = this;
axios.get('/admin/getTags').then(function(result){
let data = result.data;
for(let i in data) {
vm.tagsObject.push({id: data[i].id, name: data[i].name});
}
});
},
addTag (newTag) {
const tag = {
name: newTag,
id: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
}
this.tagsObject.push(tag);
this.tags.push(tag);
},
selectTags(value) {
this.selected_tags = value.map(a=>a.id);
},
onFileChange(e) {
const file = e.target.files[0];
this.url = URL.createObjectURL(file);
this.selected_cover = file;
},
saveBook() {
const fd = new FormData();
fd.append('image', this.selected_cover, this.selected_cover.name)
console.log(this.selected_cover);
var book_details = {
'title': this.book_title,
'description': this.book_description,
'book_cover': this.selected_cover,
'tags': this.selected_tags
};
axios.post('/admin/saveBook', book_details).then(function(result){
console.log('done')
})
}
},
created() {
this.getTags();
}
}
</script>
<!-- New step!
Add Multiselect CSS. Can be added as a static asset or inside a component. -->
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
and here's my controller
public function store(Request $request)
{
$this->validate(request(), [
'title' => 'required|min:5',
'description' => 'required|min:10',
'book_cover' => 'required|image|mimes:jpeg,jpg,png|max:10000'
]);
// File Upload
if($request->hasFile('book_cover')) {
$fileNameWithExt = $request->file('book_cover')->getClientOriginalName();
// GET FILE NAME
$filename = pathinfo($fileNameWithExt, PATHINFO_FILENAME);
// GET EXTENSION
$extension = $request->file('book_cover')->getClientOriginalExtension();
// File Unique Name
$fileNameToStore = $filename. '_'. time().'.'.$extension;
$path = $request->file('book_cover')->storeAs('public/book_covers', $fileNameToStore);
} else {
$fileNameToStore = 'noimage.jpg';
}
$book = new Book;
$book->title = request('title');
$book->description = request('description');
$book->book_cover = $fileNameToStore;
$book->save();
$book->tags()->sync($request->tags, false);
return back()->with('success', 'Book Created Successfully!');
}
I never touched my controller because this is what I used when I do this feature in Laravel Way but when I save it, the details are being saved but the image is not uploading instead it saves noimage.jpg in the database. Does anyone know what I am doing wrong?
i tried to add this part const fd = new FormData(); in book_details but when i console.log(fd) it returned no data.
you should pass the fd object and not the book_details in your POST request.
you could do it something like this.
onFileChange(e) {
const file = e.target.files[0];
// this.url = URL.createObjectURL(file);
this.selected_cover = file;
},
saveBook() {
const fd = new FormData();
fd.append('image', this.selected_cover)
fd.append('title', this.book_title)
fd.append('description', this.book_description)
fd.append('book_cover', URL.createObjectURL(this.selected_cover))
fd.append('tags', this.selected_tags)
axios.post('/admin/saveBook', fd).then(function(result){
console.log('done')
})
}
and also, you can't just console.log the fd in the console. what you can do instead is something like this
for (var pair of fd.entries()) {
console.log(pair[0]+ ', ' + pair[1]);
}
FormData is a special type of object which is not stringifyable and cannot just be printed out using console.log. (link)
Multiple file upload using vue js in laravel my validation is like
$request->validate([
'title' => 'required',
'pics.*' => 'required|image|mimes:jpeg,png,jpg,gif'
]);
and my component is like below
<template>
<div class="col-md-6">
<div class="form-group">
<label>Title</label>
<input id="title" type="text" ref="myDiv" v-model="title" class="form-control" name="title">
<div v-cloak><label class="error" v-if="errors['title']">{{ errors.title[0]
}}</label></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Upload Files</label>
<input id="uploadfile" type="file" ref="pics" name="pics[]" multiple class="form-control" #change="fieldChange">
<div v-cloak><label class="error" v-if="errors['pics']">{{ errors.pics[0] }}</label></div>
</div>
</div>
</template>
export default {
data(){
return {
attachments:[],
pics : [],
errors: [],
form: new FormData
}
},
methods:{
fieldChange(e){
let selectedFiles=e.target.files;
if(!selectedFiles.length){
return false;
}
for(let i=0;i<selectedFiles.length;i++){
this.attachments.push(selectedFiles[i]);
}
console.log(this.attachments);
},
uploadFile() {
this.errors = [];
this.form.append('img',this.attachments2);
if(this.attachments.length > 0){
for(let i=0; i<this.attachments.length;i++){
this.form.append('pics[]',this.attachments[i]);
}
}else {
this.form.append("pics", '');
}
//this.form.append('')
const config = { headers: { 'Content-Type':
'multipart/form-data' } };
axios.post('/admin/theme/',this.form,config).then(response=>
{
this.pics = [];
}).catch((error) => {
this.errors = error.response.data.errors;
console.log(this.errors.pics);
});
}
},
mounted() {
console.log('Component mounted.')
}
}
If i click to submit button empty title error print but multiple file error not print, i get error but not print
pics.0:["The pics.0 field is required."]
But title field validation error print perfectly
Please anyone help me to print multiple file upload error using vue js
I have an component with text input. After fill input and submit form i sand axios query and after response i need stay on the same page with error popup or include new component with response data.
my component
<template>
<div class="col-md-12">
<div class="form-container">
<form v-on:submit="prepareCollage()" class="main-form">
<div class="form-group">
<input type="hidden" name="_token" value="">
<input placeholder="your text" v-model="text" name="query" type="text" class="form-control">
</div>
<button class="go btn btn-primary">Go!</button>
</form>
</div>
</div>
</template>
<script>
export default {
data () {
return {
text : '',
}
},
methods: {
prepareCollage(){
event.preventDefault();
axios.get('/api/prepare?query='+encodeURIComponent(this.text))
.then(function(result){
const result_data = result.data;
// if controller gave an error
if(result_data.error === true){
let error_text = result_data.error_text;
if(typeof(result_data.with_link) != 'undefined' && result_data.with_link.length > 0){
error_text += "<a href='"+result_data.with_link+"'>"+result_data.link_text+"</a>";
}
Vue.swal({
title: 'Error!',
html: error_text,
type: 'error',
})
}else{
// here i need include new component
}
});
}
}
}
</script>
in "else" block i have to include new vue component with data from this component. I have never used vuejs and have difficulty with understand this.