How to update Image in vue - vue.js

I'm a newbie in vue, I need help to update image, I'm using vform, in below onFileSelected function responsible creating and updating image, I'm successfully creating data with image, but now I'm stack in update the image, here is my code with form structure
Form
<form #submit.prevent="editMode ? update() : store()">
<div class="form-group">
<label for="image" class="font-weight-bold">Image</label>
<input class="form-control" type="file"
:class="{ 'is-invalid': form.errors.has('image') }"
name="image" id="image" accept="image/*"
#change="onFileSelected">
<has-error :form="form" field="image"></has-error>
</div>
</form>
I'm storing like this
onFileSelected(event) {
let file = event.target.files[0];
this.form.image = file;
},
store() {
this.$Progress.start();
this.form.busy = true;
this.form.post('/api/students', {
transformRequest: [function (data, headers) {
return objectToFormData(data)
}],
}).then(response => {
//......
}),
My edit code is
edit(student) {
this.editMode = true;
this.clearForm();
this.form.fill(student);
$('#modal').modal('show');
},
update() {
this.$Progress.start();
this.form.busy = true;
this.form.patch('/api/students/' + this.form.id)
.then(response => {
//.........
})
.catch(e => {})
},

I think your issue is that while your data is updating the render is not. For this you can put a key attribute (with some number or id) on the element that contains the image and then inside the update function once updated change the key like: forceReloadKey++ this will force the component to rerender and update.
Like so:
<form :key="reloadKey" #submit.prevent="editMode ? update() : store()">
...
update() {
...
this.reloadKey++
...

Related

add comment using v-model inside v-for loop in posts

I'm getting a posts array from a vuex getter and looping through them with v-for to display each post and it's comment then i added an input field and binded it with v-model to get the input value and dispatch an action to send the value to the API
<div class="post-content" v-for="(post, index) in posts">
<div class="post-comment">
<input type="text" class="form-control" placeholder="Add a comment" v-model="comment" #keyup.enter="addComment(post.id)">
</div>
</div>
<script>
export default {
data() {
return {
postContent: '',
comment: ''
}
},
methods: {
addPost() {
this.$store.dispatch('addPost', {
content: this.postContent
})
this.postContent = ''
},
addComment(postID, index) {
this.$store.dispatch('addComment', {
body: this.comment,
post_id: postID
})
}
},
created(){
this.$store.dispatch( 'loadFeed' )
},
computed: {
postsLoadStatus(){
return this.$store.getters.getPostsLoadStatus
},
posts(){
return this.$store.getters.getFeed
}
},
}
</script>
but when i set the v-model to a data property and try to type something in the input it's assigned on all posts so what's the right way to grab the comment data
Create a getter that accepts a function:
getters () {
getCommentByPostId(state) => (post_id) => {
return state.posts.find((post) => post.id === post_id).comment
}
}
Then use that getter on that :value and not v-model:
<input type="text" class="form-control" placeholder="Add a comment" :value="$store.getters['getCommentByPostId'](post.id)" #keyup.enter="addComment(post.id)">
Make sure to handle scenarios where the comment doesn't exist and return an empty string, too.

How does vuejs react to component data updated asynchronously

I am very new with vuejs and recently started to try to replace some old jquery code that I have and make it reactive with vuejs. The thing is I have a component that gets information from a nodejs server via socket.io asynchronously.
When I get the data and update my component's data I see the changes when I console log it but it does not change the DOM the way I want it to do.
What is the proper way to grab data asynchronously and use it inside a component? I post some parts of my code so you can see it. I will appreciate any advice you can give me. Thanks in advance!
Vue.component('chat', {
data() {
return {
chat: null,
commands: [],
chatOpened: false,
}
},
props: [
'io',
'messages',
'channels',
'connectChat',
'roomChat',
'user',
'userId',
'soundRoute',
],
methods: {
openChat() {
this.chatOpened = true;
},
closeChat() {
this.chatOpened = false;
},
},
created() {
this.chat = this.$io.connect(this.connectChat);
this.commands.push('clear');
let self = this;
$.each(this.channels, function(index, value) {
self.chat.emit('join', {room: index, user: self.user, userId: self.userId}, function(err, cb) {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
self.channels[index].loaded = true;
}
//some more code
}
});
});
console.log(this.channels);
},
template: `
<div>
<div id="container-chat-open-button" #click="openChat" :class="{hide : chatOpened}">
<div>+90</div>
<i class="fas fa-comment-alt"></i>
</div>
<div id="container-chat" class="chat__container" :class="{open : chatOpened}">
<div id="container-chat-close-button" #click="closeChat">
<span>
<div>
<i class="fas fa-comment-alt"></i>
#{{ messages.chat_lobby_icon_title }}
</div>
<i class="icon-arrowdown"></i>
</span>
</div>
<div id="alert-chat" class="chat__container-notifications animated flash"></div>
<div class="row">
<ul>
<li v-for="channel in channels" v-show="channel.loaded === true">Channel loaded</li>
</ul>
</div>
</div>
</div>
`
});
I would expect to see the list of channels with messsages but instead I don't see the list even thought I see my channels with the loaded attribute set to true (by default they all have this attribute set to false).
My guess is that it's this part that is not working as expected.
if (messages.length > 0) {
self.channels[index].loaded = true;
}
The reactive way of doing this is by setting the full object again.
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
EDIT 1:
this.channels.forEach((channel) => {
this.chat.emit('join', {room: index, user: self.user, userId: self.userId}, (err, cb) => {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
}
//some more code
}
});
})
You'll need to add support for the rest-spread-operator using babel.

Retrieve data attribute value of clicked element with v-for

I've made a datalist which is filled dynamically and it works correctly.
Now, I need listen the click event on the options to retrieve the data-id value and put it as value in the input hidden.
I already tried with v-on:click.native and #click but there is no response in the console.
Any idea? I'm just starting at Vue, hope you can help me.
Edit:
Looks like it doesn't even fire the function. I've tried v-on:click="console.log('Clicked')" but nothing happens.
<input type="hidden" name="id_discipline" id="id_discipline">
<input list="disciplines" id="disciplines-list">
<datalist id="disciplines">
<option
v-for="discipline in disciplines"
:key="discipline.id_discipline"
:data-id="discipline.id_discipline"
v-on:click="updateDisciplineId($event)"
>{{discipline.name}}</option>
</datalist>
methods: {
updateDisciplineId(event) {
console.log('clicked!);
}
},
Using datalist is not suited for what you want to acheive, however there's a workaround with a limitation.
Template:
<template>
<div>
<input
type="text"
name="id_discipline"
v-model="selectedID"
placeholder="Data id value of clicked"
/>
<input
#input="onChange"
list="disciplines"
id="disciplines-list"
class="form-control"
placeholder="Seleccionar disciplina"
/>
<datalist id="disciplines">
<option
v-for="discipline in disciplines"
:key="discipline.id_discipline"
:data-value="discipline.id_discipline"
>{{ discipline.name }}</option
>
</datalist>
</div>
</template>
Script Part:
<script>
export default {
data() {
return {
selectedID: "",
id_discipline: "",
disciplines: [
{
id_discipline: 1,
name: "Yoga"
},
{
id_discipline: 2,
name: "Functional"
}
]
};
},
methods: {
onChange(e) {
this.getID(e.target.value).then(
resposnse => (this.selectedID = resposnse)
);
},
async getID(value) {
let promise = new Promise((resolve, reject) => {
this.disciplines.forEach(item => {
if (item.name === value) resolve(item.id_discipline);
});
});
return await promise;
}
}
};
</script>
Here's a working Sandbox demo.
**Limitation: Discipline name (Yoga, functional) should be unique.

Vue.js - v-model not tracking changes with dynamic data?

I have the following form where the input fields are dynamically generated however when I update the fields the two way binding isn't happening - nothing is changing when i view the results in dev tools?
<template v-for="field in formFields" :key="field.name">
<div class="form-group" v-if="field.type == text'">
<label class="h4" :for="field.label" v-text="field.label"></label>
<span class="required-asterisk" v-if="field.required"> *</span>
<input :class="field.className"
:id="field.name"
:name="field.name"
type="text"
:maxlength="!!field.maxLength ? field.maxLength : false"
v-validate="{ required: field.required}"
:data-vv-as="field.label"
v-model="form[field.name]"/>
<span class="field-validation-error" v-show="errors.has(field.name)" v-text="errors.first(field.name)"></span>
</div>
</template>
And the following vue instance:
export default {
props: ['formFields'],
data: function () {
return {
form: {},
}
},
created: function() {
this.resetForm();
},
methods: {
resetForm: function() {
this.form = {
'loading': false
}
_.each(this.formFields, (field) => {
this.form[field.name] = field.value;
});
$('#editModal').modal('hide');
this.errors.clear();
}
}
}
When I hard code the values in the form it seems to work:
this.form = {
'loading': false,
'Subject': 'Test',
'Author': 'Roald Dahl'
}
So it seems like something to with the following which it doesn't like:
_.each(this.formFields, (field) => {
this.form[field.name] = field.value;
});
Could it be something to do with the arrow function. Any ideas chaps?
You're running into a limitation of Vue's reactivity, which is spelled out in the documentation
Instead of
this.form[field.name] = field.value;
use
this.$set(this.form, field.name, field.value);
Trying changing the following:
<div class="form-group" v-if="field.type 'text'"> ->
<div class="form-group" v-if="field.type == 'text'">
and the model data object like this
data: {
form: {},
},
https://jsfiddle.net/Jubels/eywraw8t/373132/ Example here. For testing purposes I removed the validation
Instead of
this.form[field.name] = field.value;
use
this.$set(this.form, field.name, field.value);
this.form.splice(field.name, 1, field.value)
or
Vue.set(this.form, field.name, field.value);
this.form.splice(field.name, 1, field.value)
More information in : https://v2.vuejs.org/v2/guide/list.html#Caveats

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!