How to reset form elements with VueJS and Vuex - vue.js

I have a "Question and Answer" component written in VueJs, with a Vuex store. Each answer is a <textarea> element, such as the following:
<textarea class="form-control" rows="1" data-answer="1" :value="answer(1)" #change="storeChange"></textarea>
As you can see the value of the control is set by calling an answer() method and passing the question number as a parameter.
When the answer is changed the storeChange method is called and the changes are cached in a temporary object (this.changes) per the following code:
props : [
'questionnaire'
],
methods : {
answer(number) {
if (this.questionnaire.question_responses &&
(number in this.questionnaire.question_responses)) {
return this.questionnaire.question_responses[number];
}
return null;
},
storeChange(e) {
Vue.set(this.changes, e.target.dataset.answer, e.target.value);
},
save() {
// removed for clarity
},
reset() {
// what to do here?
},
}
If the user clicks the save button I dispatch an action to update the store.
If the user wants to reset the form to its original state, I need to clear this.changes, which is no problem, but I also need to 'refresh' the values from the store. How do I do this?
Note that the source of the initial state, questionnaire, comes via a prop, not a computed property that maps directly to the store. The reason for this is that there can be multiple "Question and Answer" components on one page, and I found it easier to pass the state this way.

we can by using refs reset form , example
form textarea
<form ref="textareaform">
<textarea
class="form-control"
rows="1"
data-answer="1"
:value="answer(1)"
#change="storeChange"
>
</textarea>
<button #click="reset">reset</button>
</form>
reset
reset() {
// ref='textareaform'
// reset() method resets the values of all elements in a form
// document.getElementById("form").reset();
this.$refs.textareaform.reset()
},

Related

Vue JS - Trigger Method if input lost focus

Quick question about Vue JS.
On my website, I have got a shopping cart and the user can enter any quantity. I am saving this quantity to the database table when he enters it...
issue is, the input field keeps firing save method every single digit of user types. For example, if the user types 123, the save method called 3 times. 1 and 12 and 123
I just want to call this save method when this input loses the focus, so I can save only once, not every single digit.
Component
Vue.component('product-counter', {
props: ['quantity'],
data: function () {
return {
count: this.quantity
}
},
template: `
<div class="input-group ">
<input v-bind:value="quantity" v-on:input.focus="$emit('input', $event.target.value)" type="text" class="form-control col-2">
</div>
`
})
Component Call
<product-counter
v-bind:productcode="item.productCode"
v-bind:quantity="item.quan"
v-on:input="item.quan=updateItemQuantity($event,item.productCode)"
v-on:increment-quantity="item.quan=updateItemQuantity(item.quan,item.productCode)"
v-on:decrement-quantity="item.quan=updateItemQuantity(item.quan,item.productCode)"></product-counter>
Vue: Method
"updateItemQuantity": function (totalquantity, pcode) {
if (totalquantity != '') {
... Update Database...
}
}
You're listening to the input event, which is triggered every time the value of the input changes, so every time a character is typed or removed.
Instead, you should listen to the blur event, which only fires when an input loses focus.
You can pass this along through your component, the same way you pass through the input event.
TLDR: Couple UpdateItemQuantity to v-on:blur instead of v-on:input, and make sure to $emit the blur event from your products-counter component.
Tip: Separate the client-side state (item.quan) and your server-side 'state' (your database) into two different methods. You want the value to reflect what the user is typing in real-time (input), which conflicts with what you want for updating the database (not real-time, only on blur). Otherwise you may get cases where users can't see what they type, as item.quan is never updated.
I think you just need to use #change instead of #input
It could be that you should use blur event.
Vue:
new Vue({
el: "#app",
data: {
quantity: ''
},
methods: {
printConsole: function () {
console.log('blured');
}
}
})
html:
<div id='app'>
<input v-bind:value="quantity" v-on:blur="printConsole" type="text" class="form-control col-2">
</div>
see jsfiddle for reference: https://jsfiddle.net/erezka/h8g62xfr/11/
blur will emit your change only after you focus out of the input

v-model not always updating in Vue

Short question
The v-model which binds a string to an input field won't update in some cases.
Example
I am using Vue within a Laravel application. This is the main component which contains two other components:
<template>
<div>
<select-component
:items="items"
#selectedItem="updateSelectedItems"
/>
<basket-component
:selectedItems="selectedItems"
#clickedConfirm="confirm"
#clickedStopAll="stopAll"
/>
<form ref="chosenItemsForm" method="post">
<!-- Slot for CSRF token-->
<slot name="csrf-token"></slot>
<input type="text" name="chosenItems" v-model="selectedItemsPipedList" />
</form>
</div>
</template>
<script>
export default {
props: ["items"],
data: function() {
return {
selectedItems: [],
selectedItemsPipedList: ""
};
},
methods: {
updateSelectedItems: function(data) {
this.selectedItems = data;
this.selectedItemsPipedList = this.selectedItems
.map(item => item.id)
.join("|");
},
confirm() {
this.$refs.chosenItemsForm.submit();
},
stopAll() {
this.updateSelectedItems([]);
this.confirm();
}
}
};
</script>
The method updateSelectedItems is called from the select-component and it works fine. In the end, the selectedItemsPipedList contains the selected items from the select-component, which looks like "1|2|3" and this value is bound to the input field in the chosenItemsForm. When the method confirm is called from the basket-component, this form is posted to the Laravel backend and the post request contains the chosen items as piped list. So far, so good.
The method stopAll is called from the basket-component and it will remove all the selected items from the array. Therefore it will call the method updateSelectedItems with an empty array, which will clear the selectedItems array and then clear the selectedItemsPipedList. After that, confirm is called which will post the form again. But, the post value still contains the selected items (e.g. '1|2|3'), instead of "". It looks like the v-model in my form is not updated, which is strange because it does work when selecting items. Why is it working when adding items, and doesn't when removing all items?
I believe you have a timing issue here. The value of the properties haven't been propagated to the DOM yet, so the form submission is incorrect. Try this instead:
stopAll() {
this.updateSelectedItems([]);
//NextTick waits until after the next round of UI updates to execute the callback.
this.$nextTick(function() {this.confirm()});
}

V-model binding to generated input text field

Dynamic V-models created during an ajax request doesn't update when I try inputting a value
I'm using vue2.x and axios. I want to get the value set in generated input when user submit the form. I managed to set v-model on this input during ajax request
I receive this HTLM as response:
<input type="text" value="" v-model="generatedcode">. But after submitting the form the value is still empty. Looks like Vue ignore the v-model directive. How can I fix it ?
Here is my code :
VUE
var app = new Vue({
el: '#subcribtionform',
data: {
generatedform:'',
generatedcode:''
},
methods:{
OnSuccess(response){
this.generatedform = response.data;
},
OnclickSub(){
axios.post('/submitformURL',{
lastname: this.lastname,
generatedcode: this.generatedcode,
})
}
created: function () {
axios.get('/generate_inputURL').then(this.OnSuccess);
}
HTML
<div v-html="generatedform"></div>
GENERATED INPUT
<input type="text" value="" v-model="generatedcode"/>
Try:
created: function () {
axios.get('/generate_inputURL').then(res => this.OnSuccess(res));
}
Component data must be function not object. You should be seeing warning about this in console. I guess your component is not reactive data then, which means that is not redrawn after on request done.
data(): {
return {
generatedform:'',
generatedcode:''
}
}
As I can see, you want to change the vue-app template using v-html attr - I think this is not possible. While mounting the application, the template compiles into render function, so your trick does not make any sense. You can try to do the following:
Construct the template (using html recieved from server as you want ) as string or as hidden el in the DOM
Set it in your app object - template:your_html_template
Create vue app

vueJS- prop is being mutated when local copy updates

My child component template has two input elements with v-models as below.
<input v-model="user.name" />
<input v-model="user.email" />
props: {
'account': {}
},
data: function() {
return { user : this.account.user }
}
When ever the text in the input fields changes, The user object is being updated which is expected. But the prop account.user is also updating with the changes. How can i make it to update just the user object and keep the prop account.user as it is?
This happens because in Javascript, object is passed by reference. When you do user : this.account.user, you are passing the object reference to user data. That's why when you edit user data, account.user gets edited as well, they refer to the same object.
You can clone it using ES6 spread operator.
return { user : {...this.account.user} }
If you are not using ES6, i would suggest you to use lodash clone instead.
return { user : _.clone(this.account.user) }
(Btw, the cloning methods above only works for shallow object. For deeply nested object, use lodash cloneDeep instead.)

How to bind input field and update vuex state at same time

Im coming from a React background and it's simply enough to set your state from a prop and you could call setState({...}) to update the state, so, with vue / vuex, I find it difficult.
To simplify:
Vuex State
name: "Foo bar"
Vuex Action
addName
I can change the state no problem but I need to bind an input field and when change, the state is updated. Think of this as an update form where the user details are already pre-filled and they can change their name.
<input #change="addName(newName) v-model="newName" />
I could add a watch to watch for newName and update the state but, I need to pre-fill the input with the state. Ha! I could use beforeMount() but my state is not loaded as yet.
computed: {
...mapState([
'name'
]),
},
beforeMount() {
// this.newName = this.name
console.log('Mounted') // Shows in console
console.log(this.name) // nothing
}
Name shows in templete <pre>{{ name }}</pre>
Yo can use a computed setter
computed:{
name:{
get: function(){
return store.state.name;
},
set: function(newName){
store.dispatch('addName',newName);
}
}
}
enter code here
And set the v-model to the computed property name in your <input> tag :
<input v-model="name" />
Here is the working jsfiddle