I'm trying to show a preview image when a user uploads an image. For some reason my code works when I use an <input> with regular Bootstrap class of form-control, but when I use Bootstrap-Vue's <b-form-file> I get an error: Error in render: "TypeError: Cannot read property 'name' of undefined"
Data:
data() {
return {
options: [
{ text: "Full Time", value: "Full Time" },
{ text: "Part Time", value: "Part Time" },
],
profileData: {
photo: '',
employment_type: "Full Time",
date_of_birth: "",
experience: "",
skills: "",
},
};
},
my method:
methods: {
attachImage() {
this.profileData.photo = this.$refs.profilePhoto.files[0];
let reader = new FileReader();
reader.addEventListener('load', function() {
this.$refs.profilePhotoDisplay.src = reader.result;
}.bind(this), false);
reader.readAsDataURL(this.profileData.photo);
},
}
Bootstrap Code, this code works:
<div role="group" class="form-group">
<label for="photo" class="d-block">Upload Profile Photo (optional)</label>
<div v-if="profileData.photo.name">
<img src="" ref="profilePhotoDisplay" class="w-100px" />
</div>
<input
id="photo"
v-on:change="attachImage"
ref="profilePhoto"
type="file"
class="form-control"
>
</div>
Bootstrap-Vue Code, this gives me the error:
<div v-if="profileData.photo.name">
<img src="" ref="profilePhotoDisplay" width="140" />
</div>
<b-form-group label="Upload Profile Photo (optional)" label-for="image">
<b-form-file
id="photo"
v-on:change="attachImage"
ref="profilePhoto"
type="file"
></b-form-file>
</b-form-group>
Related
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 have made this code, I want to facilitate user to dynamically enter text and display fields which have values, but I'm unable to get the result when I run it laravel+VueJs component view. below is my code
<template>
<div>
<div>
<label>Name</label>
<input type="text" #change="addRow">
</div>
<div> <label>Email</label>
<input type="text" #change="addRow1">
</div>
<div v-for="row in rows" :key="row.id">
<button-counter :id="row.id" :value="row.value"></button-counter>
</div>
</div>
<script type="text/javascript">
Vue.component('button-counter', {
props: {
value: {
default: ''
}
},
template: '<input type="text" style="margin-top: 10px;" v-model="value" >',
})
export default {
mounted() {
console.log('Component mounted.')
},
data: {
rows: [],
count:0
},
methods: {
addRow: function () {
var txtCount=1;
id='txt_'+txtCount;
this.rows.push({ value:'MyName' , description: "textbox1", id });
},
addRow1: function () {
var txtCount=1;
id='txt2_'+txtCount;
this.rows.push({ value: "myEmail", description: "textbox2", id });
}
}
}
data should be a function that returns an object holding data you want to manipulate, and you need to define the id as a variable, here's a working Vue SFC
<template>
<div>
<div>
<label>Name</label>
<input type="text" #change="addRow" />
</div>
<div>
<label>Email</label>
<input type="text" #change="addRow1" />
</div>
<div v-for="row in rows" :key="row.id">
<button-counter :id="row.id" :value="row.value"></button-counter>
</div>
</div>
</template>
<script type="text/javascript">
Vue.component("button-counter", {
props: {
value: {
default: ""
}
},
template: '<input type="text" style="margin-top: 10px;" v-model="value" >'
});
export default {
data() {
return {
rows: [],
count: 0
};
},
methods: {
addRow: function() {
var txtCount = ++this.count;
let id = "txt_" + txtCount;
this.rows.push({ value: "MyName", description: "textbox1", id });
},
addRow1: function() {
var txtCount = ++this.count;
let id = "txt2_" + txtCount;
this.rows.push({ value: "myEmail", description: "textbox2", id });
}
}
};
</script>
Hope this helps
I have a list of nested comments. Under each comment, I'd like to add a "reply" button that, when click, show a reply form.
For now, everytime I click a "reply" button, it shows the form. But the thing is, I'd like to show only one form on the whole page. So basically, when I click on "reply" it should close the other form alreay opened and open a new one under the right comment.
Edit :
So I was able to make some slight progress. Now I'm able to only have one active form opening on each level of depth in the nested loop. Obviously, what I'm trying to do now is to only have one at all.
What I did was emitting an event from the child component and handle everything in the parent component. The thing is, it would work great in a non-nested comment list but not so much in my case...
Here is the new code:
In the parentComponent, I have a handleSelected method as such:
handleSelected (id) {
if(this.selectedItem === id)
this.selectedItem = null;
else
this.selectedItem = id;
},
And my childComponent:
<template>
<div v-if="comment">
<div v-bind:style=" iAmSelected ? 'background: red;' : 'background: none;' ">
<p>{{ comment.author.name }}<br />{{ comment.created_at }}</p>
<p>{{ comment.content }}</p>
<button class="button" #click="toggle(comment.id)">Répondre</button>
<button class="button" #click="remove(comment.id)">Supprimer</button>
<div v-show="iAmSelected">
<form #submit.prevent="submit">
<div class="form-group">
<label for="comment">Votre réponse</label>
<textarea class="form-control" name="comment" id="comment" rows="5" v-model="fields.comment"></textarea>
<div v-if="errors && errors.comment" class="text-danger">{{ errors.comment[0] }}</div>
</div>
<button type="submit" class="btn btn-primary">Envoyer</button>
<div v-if="success" class="alert alert-success mt-3">
Votre réponse a bien été envoyée !
</div>
</form>
</div>
</div>
<div v-if="comment.hasReply">
<div style="margin-left: 30px;">
<comment v-for="comment in comments"
:key="comment.id"
:comment="comment" #remove-comment="remove"
:is-selected="selectedItem" #selected="handleSelected($event)">
</comment>
</div>
</div>
</div>
</template>
<script>
import comment from './CommentItem'
export default {
name: 'comment',
props: {
isSelected: Number,
comment: {
required: true,
type: Object,
}
},
data () {
return {
comments: null,
fields: {},
errors: {},
success: false,
loaded: true,
selectedItem: null,
}
},
computed: {
iAmSelected () {
return this.isSelected === this.comment.id;
}
},
methods: {
remove(id) {
this.$emit('remove-comment', id)
},
toggle(id) {
this.$emit('selected', id);
},
handleSelected(id) {
if(this.selectedItem === id)
this.selectedItem = null;
else
this.selectedItem = id;
},
},
mounted(){
if (this.comment.hasReply) {
axios.get('/comment/replies/' + this.comment.id)
.then(response => {
this.comments = response.data
})
}
}
}
</script>
Thanks in advance for your help!
My page exist of a table where I can add new rows. If you want to add a new row a pop-up window appear where the new values can be added.
This new data is then saved to the database after submitting. If I again want to add a new row the input fields, they should be cleared.
The method I use, is working but isn't very clear.
Note: My code shows only a part of the input fields, to make it more clear. My pop-up window actually contains 20 input fields.
I would like to clear them all at once instead of clearing them one by one (like I am doing now).
Because I am already doing this for defining the v-model, pushing the new data to the database directly on the page and via post axios request.
Is there a cleaner way to do this?
Thanks for any input you could give me.
This is my code:
html part
<div class="col-2 md-2">
<button class="btn btn-success btn-sx" #click="showModal('add')">Add New</button>
<b-modal :ref="'add'" hide-footer title="Add new" size="lg">
<div class="row" >
<div class="col-4">
<b-form-group label="Category">
<b-form-input type="text" v-model="newCategory"></b-form-input>
</b-form-group>
</div>
<div class="col-4">
<b-form-group label="Name">
<b-form-input type="text" v-model="newName" placeholder="cd4"></b-form-input>
</b-form-group>
</div>
<div class="col-4">
<b-form-group label="Amount">
<b-form-input type="number" v-model="newAmount" ></b-form-input>
</b-form-group>
</div>
</div>
<div class="row" >
<div class="col-8">
</div>
<div class="col-4">
<div class="mt-2">
<b-button #click="hideModal('add')">Close</b-button>
<b-button #click="storeAntibody(antibodies.item)" variant="success">Save New Antibody</b-button>
</div>
</div>
</div>
</b-modal>
</div>
js part
<script>
import { async } from 'q';
export default {
props: ['speciedata'],
data() {
return {
species: this.speciedata,
newCategory: '',
newName: '',
newAmount:'',
}
},
computed: {
},
mounted () {
},
methods: {
showModal: function() {
this.$refs["add"].show()
},
hideModal: function(id, expId) {
this.$refs['add'].hide()
},
addRow: function(){
this.species.push({
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
},
storeSpecie: async function() {
axios.post('/specie/store', {
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
.then(this.addRow())
// Clear input
.then(
this.newName = '',
this.newCategory = '',
this.newAmount = '',
)
.then(this.hideModal('add'))
},
}
}
</script>
in your data of vuejs app , you have to set one object for displaying modal data like modalData then to reset data you can create one function and set default value by checking type of value using loop through modalData object keys
var app = new Vue({
el: '#app',
data: {
message:"Hi there",
modalData:{
key1:"value1",
key2:"value2",
key3:"value3",
key4:5,
key5:true,
key6:"val6"
}
},
methods: {
resetModalData: function(){
let stringDefault="";
let numberDefault=0;
let booleanDefault=false;
Object.keys(this.modalData).forEach(key => {
if(typeof(this.modalData[key])==="number"){
this.modalData[key]=numberDefault;
}else if(typeof(this.modalData[key])==="boolean") {
this.modalData[key]=booleanDefault;
}else{
// default type string
this.modalData[key]=stringDefault;
}
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
{{modalData}}
<br/>
<button #click="resetModalData">Reset Modal Data</button>
</div>
update : in your case :
data:{
species: this.speciedata,
modalData:{
newCategory: '',
newName: '',
newAmount:''
}
},
and after storing data :
storeSpecie: async function() {
axios.post('/specie/store', {
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
.then(()=>{
this.addRow();
this.resetModalData();
this.hideModal('add')
}
},
In native Javascript you get the reset() method.
Here is how it is used :
document.getElementById("myForm").reset();
It will clear every input in the form.
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>