computed property set not called in Vue - javascript

According to the documentation I should be able to use computed properties as v-model in Vue as long as I define get/set methods, but in my case it doesn't work:
export default{
template: `
<form class="add-upload" #submit.prevent="return false">
<label><input type="checkbox" v-model="options.test" /> test </label>
</form>
`,
computed: {
options: {
get(){
console.log('get');
return {test: false};
},
set(value){
console.log('set');
},
},
}
}
Apparently set is not called when I check/uncheck the input.
But get is called when the component is displayed...

Edit: After reading in the comments that you rely on the localstorage, I can only suggest you to take the Vuex approach and use a persistence library to handle the localstorage. (https://www.npmjs.com/package/vuex-persist)
This way, your localstorage will always be linked to your app and you don't have to mess with getItem/setItem everytime.
Looking at your approach, I assume you have your reasons to use a computed property over a data property.
The problem happens because your computed property returns an object defined nowhere but in the get handler.
Whatever you try, you won't be able to manipulate that object in the set handler.
The get and set must be linked to a common reference. A data property, as many suggested, or a source of truth in your app (a Vuex instance is a very good example).
this way, your v-model will work flawlessly with the set handler of your computed property.
Here's a working fiddle demonstrating the explanation:
With Vuex
const store = new Vuex.Store({
state: {
// your options object is predefined in the store so Vue knows about its structure already
options: {
isChecked: false
}
},
mutations: {
// the mutation handler assigning the new value
setIsCheck(state, payload) {
state.options.isChecked = payload;
}
}
});
new Vue({
store: store,
el: "#app",
computed: {
options: {
get() {
// Here we return the options object as depicted in your snippet
return this.$store.state.options;
},
set(checked) {
// Here we use the checked property returned by the input and we commit a Vuex mutation which will mutate the state
this.$store.commit("setIsCheck", checked);
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
<div id="app">
<h2>isChecked: {{ options.isChecked }}</h2>
<input type="checkbox" v-model="options.isChecked" />
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex#2.0.0"></script>
With a data property
new Vue({
el: "#app",
data: {
options: {
isChecked: false
}
},
computed: {
computedOptions: {
get() {
return this.options;
},
set(checked) {
this.options.isChecked = checked;
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
<div id="app">
<h2>isChecked: {{ computedOptions.isChecked }}</h2>
<input type="checkbox" v-model="computedOptions.isChecked" />
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Your approach is a bit special IMHO but, again, you must have your reasons to do so.

The very simple explanation here in code. computed properties are dependent on other data/reactive variables. If only when the reactive properties changed their values and if same property used to compute some other computed properties then the computed property would become reactive.
this way we must set values and get in setter and getter methods.
new Vue({
el: '#app',
data: {
message: 'Use computed property on input',
foo:0,
isChecked:true
},
computed:{
bar:{
get: function(){
return this.foo;
},
set: function(val){
this.foo = val;
}
},
check:{
get: function(){
return this.isChecked;
},
set: function(val){
this.isChecked = val;
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ message }} Text</p>
<input type="text" v-model="bar" />
{{bar}}
<br/>
<p>{{ message }} Checkbox</p>
<input type="checkbox" v-model="check" />
{{check}}
</div>

Instead of a computed getter/setter, use a local data prop, initialized to the target localStorage item; and a deep watcher (which detects changes on any subproperty) that sets localStorage upon change. This allows you to still use v-model with the local data prop, while observing changes to the object's subproperties.
Steps:
Declare a local data prop (named options) that is initialized to the current value of localStorage:
export default {
data() {
return {
options: {}
}
},
mounted() {
const myData = localStorage.getItem('my-data')
this.options = myData ? JSON.parse(myData) : {}
},
}
Declare a watch on the data prop (options), setting deep=true and handler to a function that sets localStorage with the new value:
export default {
watch: {
options: {
deep: true,
handler(options) {
localStorage.setItem('my-data', JSON.stringify(options))
}
}
},
}
demo

It seems the problem is both in the presence of options and the return value of the getter.
You could try this:
let options;
try {
options = JSON.parse(localStorage.getItem("options"));
}
catch(e) {
// default values
options = { test: true };
}
function saveOptions(updates) {
localStorage.setItem("options", JSON.stringify({ ...options, ...updates }));
}
export default{
template: `
<form class="add-upload" #submit.prevent="return false">
<label><input type="checkbox" v-model="test" /> test </label>
</form>`,
computed: {
test: {
get() {
console.log('get');
return options.test;
},
set(value) {
console.log('set', value);
saveOptions({ test: value });
},
},
}
}
Hope this helps.

I'm not familiar if there's a computed set method that could work here, but there's a few other approaches to solving the problem.
If you want a singular getter for mutating the data, you can use an event based method for setting the data. This method is my favorite:
export default {
template: `
<form class="add-upload" #submit.prevent="">
<label for="test"> test </label>
{{options.test}}
<input id="test" type="checkbox" v-model="options.test" #input="setOptions({test: !options.test})"/>
</form>
`,
data() {
return {
optionsData: {
test: false
}
}
},
computed: {
options: {
get() {
return this.optionsData;
},
},
},
methods: {
setOptions(options) {
this.$set(this, "optionsData", { ...this.optionsData, ...options })
}
}
}
If you're not really doing anything in the get/set you can just use the data option
export default {
template: `
<form class="add-upload" #submit.prevent="">
<label for="test"> test </label>
{{options.test}}
<input id="test" type="checkbox" v-model="options.test" />
</form>
`,
data() {
return {
options: {
test: false
}
}
}
}
Then there's also the option of get/set for every property
export default {
template: `
<form class="add-upload" #submit.prevent="">
<label for="test"> test </label>
{{test}}
<input id="test" type="checkbox" v-model="test" />
</form>
`,
data() {
return {
optionsData: {
test: false
}
}
},
computed: {
test: {
get() {
return this.optionsData.test;
},
set(value) {
this.optionsData.test = value
}
},
},
}

The return value of Vue computed properties are not automatically made reactive. Because you are returning a plain object, and because you're assigning to a property within the computed property, the setter will not trigger.
You have two problems you need to solve, one problem's solution is to store a reactive version of your computed property value (see Vue.observable()). The next problem is a bit more nuanced, I'd need to know why you want to hook in to the setter. My best guess without more information would be that you're actually looking to perform side-effects. In that case, you should watch the value for changes (see vm.$watch()).
Here's how I'd write that component based on the assumptions above.
export default {
template: `
<form class="add-upload" #submit.prevent="return false">
<label><input type="checkbox" v-model="options.test" /> test </label>
</form>
`,
computed: {
options(vm) {
return (
vm._internalOptions ||
(vm._internalOptions = Vue.observable({ test: false }))
)
},
},
watch: {
"options.test"(value, previousValue) {
console.log("set")
},
},
}
If you need to trigger side-effects based on anything changing on options, You can deeply watch it. The biggest caveat though is that the object must be reactive (solved by Vue.observable() or defining it in the data option).
export default {
watch: {
options: {
handler(value, previousValue) {
console.log("set")
},
deep: true,
},
},
}

Related

Reference Type values not updating in localStorage with two way data binding in Vue [duplicate]

According to the documentation I should be able to use computed properties as v-model in Vue as long as I define get/set methods, but in my case it doesn't work:
export default{
template: `
<form class="add-upload" #submit.prevent="return false">
<label><input type="checkbox" v-model="options.test" /> test </label>
</form>
`,
computed: {
options: {
get(){
console.log('get');
return {test: false};
},
set(value){
console.log('set');
},
},
}
}
Apparently set is not called when I check/uncheck the input.
But get is called when the component is displayed...
Edit: After reading in the comments that you rely on the localstorage, I can only suggest you to take the Vuex approach and use a persistence library to handle the localstorage. (https://www.npmjs.com/package/vuex-persist)
This way, your localstorage will always be linked to your app and you don't have to mess with getItem/setItem everytime.
Looking at your approach, I assume you have your reasons to use a computed property over a data property.
The problem happens because your computed property returns an object defined nowhere but in the get handler.
Whatever you try, you won't be able to manipulate that object in the set handler.
The get and set must be linked to a common reference. A data property, as many suggested, or a source of truth in your app (a Vuex instance is a very good example).
this way, your v-model will work flawlessly with the set handler of your computed property.
Here's a working fiddle demonstrating the explanation:
With Vuex
const store = new Vuex.Store({
state: {
// your options object is predefined in the store so Vue knows about its structure already
options: {
isChecked: false
}
},
mutations: {
// the mutation handler assigning the new value
setIsCheck(state, payload) {
state.options.isChecked = payload;
}
}
});
new Vue({
store: store,
el: "#app",
computed: {
options: {
get() {
// Here we return the options object as depicted in your snippet
return this.$store.state.options;
},
set(checked) {
// Here we use the checked property returned by the input and we commit a Vuex mutation which will mutate the state
this.$store.commit("setIsCheck", checked);
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
<div id="app">
<h2>isChecked: {{ options.isChecked }}</h2>
<input type="checkbox" v-model="options.isChecked" />
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex#2.0.0"></script>
With a data property
new Vue({
el: "#app",
data: {
options: {
isChecked: false
}
},
computed: {
computedOptions: {
get() {
return this.options;
},
set(checked) {
this.options.isChecked = checked;
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
<div id="app">
<h2>isChecked: {{ computedOptions.isChecked }}</h2>
<input type="checkbox" v-model="computedOptions.isChecked" />
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
Your approach is a bit special IMHO but, again, you must have your reasons to do so.
The very simple explanation here in code. computed properties are dependent on other data/reactive variables. If only when the reactive properties changed their values and if same property used to compute some other computed properties then the computed property would become reactive.
this way we must set values and get in setter and getter methods.
new Vue({
el: '#app',
data: {
message: 'Use computed property on input',
foo:0,
isChecked:true
},
computed:{
bar:{
get: function(){
return this.foo;
},
set: function(val){
this.foo = val;
}
},
check:{
get: function(){
return this.isChecked;
},
set: function(val){
this.isChecked = val;
}
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>{{ message }} Text</p>
<input type="text" v-model="bar" />
{{bar}}
<br/>
<p>{{ message }} Checkbox</p>
<input type="checkbox" v-model="check" />
{{check}}
</div>
Instead of a computed getter/setter, use a local data prop, initialized to the target localStorage item; and a deep watcher (which detects changes on any subproperty) that sets localStorage upon change. This allows you to still use v-model with the local data prop, while observing changes to the object's subproperties.
Steps:
Declare a local data prop (named options) that is initialized to the current value of localStorage:
export default {
data() {
return {
options: {}
}
},
mounted() {
const myData = localStorage.getItem('my-data')
this.options = myData ? JSON.parse(myData) : {}
},
}
Declare a watch on the data prop (options), setting deep=true and handler to a function that sets localStorage with the new value:
export default {
watch: {
options: {
deep: true,
handler(options) {
localStorage.setItem('my-data', JSON.stringify(options))
}
}
},
}
demo
It seems the problem is both in the presence of options and the return value of the getter.
You could try this:
let options;
try {
options = JSON.parse(localStorage.getItem("options"));
}
catch(e) {
// default values
options = { test: true };
}
function saveOptions(updates) {
localStorage.setItem("options", JSON.stringify({ ...options, ...updates }));
}
export default{
template: `
<form class="add-upload" #submit.prevent="return false">
<label><input type="checkbox" v-model="test" /> test </label>
</form>`,
computed: {
test: {
get() {
console.log('get');
return options.test;
},
set(value) {
console.log('set', value);
saveOptions({ test: value });
},
},
}
}
Hope this helps.
I'm not familiar if there's a computed set method that could work here, but there's a few other approaches to solving the problem.
If you want a singular getter for mutating the data, you can use an event based method for setting the data. This method is my favorite:
export default {
template: `
<form class="add-upload" #submit.prevent="">
<label for="test"> test </label>
{{options.test}}
<input id="test" type="checkbox" v-model="options.test" #input="setOptions({test: !options.test})"/>
</form>
`,
data() {
return {
optionsData: {
test: false
}
}
},
computed: {
options: {
get() {
return this.optionsData;
},
},
},
methods: {
setOptions(options) {
this.$set(this, "optionsData", { ...this.optionsData, ...options })
}
}
}
If you're not really doing anything in the get/set you can just use the data option
export default {
template: `
<form class="add-upload" #submit.prevent="">
<label for="test"> test </label>
{{options.test}}
<input id="test" type="checkbox" v-model="options.test" />
</form>
`,
data() {
return {
options: {
test: false
}
}
}
}
Then there's also the option of get/set for every property
export default {
template: `
<form class="add-upload" #submit.prevent="">
<label for="test"> test </label>
{{test}}
<input id="test" type="checkbox" v-model="test" />
</form>
`,
data() {
return {
optionsData: {
test: false
}
}
},
computed: {
test: {
get() {
return this.optionsData.test;
},
set(value) {
this.optionsData.test = value
}
},
},
}
The return value of Vue computed properties are not automatically made reactive. Because you are returning a plain object, and because you're assigning to a property within the computed property, the setter will not trigger.
You have two problems you need to solve, one problem's solution is to store a reactive version of your computed property value (see Vue.observable()). The next problem is a bit more nuanced, I'd need to know why you want to hook in to the setter. My best guess without more information would be that you're actually looking to perform side-effects. In that case, you should watch the value for changes (see vm.$watch()).
Here's how I'd write that component based on the assumptions above.
export default {
template: `
<form class="add-upload" #submit.prevent="return false">
<label><input type="checkbox" v-model="options.test" /> test </label>
</form>
`,
computed: {
options(vm) {
return (
vm._internalOptions ||
(vm._internalOptions = Vue.observable({ test: false }))
)
},
},
watch: {
"options.test"(value, previousValue) {
console.log("set")
},
},
}
If you need to trigger side-effects based on anything changing on options, You can deeply watch it. The biggest caveat though is that the object must be reactive (solved by Vue.observable() or defining it in the data option).
export default {
watch: {
options: {
handler(value, previousValue) {
console.log("set")
},
deep: true,
},
},
}

Accessing props in child vue component data function?

I have the below child component. The props are updated from an input selector in the parent. Why does level: this.globalForm.level not update the child's level
Parent:
<template>
<div>
<div class="form-container">
<select class="form-control" v-model="level">
<option v-for="level in options" v-bind:key="level">{{ level }}</option>
</select>
<button #click="submit()">Create</button>
</div>
<child v-bind:globalForm="globalForm"/>
</div>
</template>
<script>
inputFiled;
export default {
data() {
return {
level: "",
globalForm: {
level: ""
},
options: ["level1", "level2", "level3"]
};
},
components: {
child
},
methods: {
submit() {
this.globalForm.level = this.level;
}
},
watch: {
level() {
this.globalForm.level = this.level;
}
}
};
</script>
Child:
<template>
<div class="form-container">
<option v-for="level in options" v-bind:key="level">{{ level }}</option>
</div>
</template>
<script>
export default {
props: { globalForm: Object },
data() {
return {
options: ["level1","level2","level3",],
level: this.globalForm.level //this does not update the child's level component
};
}
};
</script>
TLDR
level should be a computed property on the child so that you can detect changes in the prop. You are setting level in the data function, so updates to the prop never make it to level.
Below you'll find a minimal example on what I think you want to achieve.
Vue.config.productionTip = false;
Vue.component('parent', {
template: `
<div class="parent">
<b>PARENT</b>
<div class="form-container">
<select class="form-control" v-model="level">
<option v-for="level in options" v-bind:key="level">{{ level }}</option>
</select>
<button #click="submit()">Create</button>
</div>
<child v-bind:globalForm="globalForm"/>
</div>
`,
data: () => ({
level: "",
globalForm: {
level: ""
},
options: ["level1", "level2", "level3"]
}),
methods: {
submit() {
this.globalForm.level = this.level;
}
}
});
Vue.component('child', {
template: `
<div class="form-container child">
<p><b>Child</b></p>
Level: {{ level }}
</div>
`,
props: {
globalForm: Object
},
computed: {
level() {
return this.globalForm.level;
}
}
});
new Vue({
el: "#app"
})
.parent {
background-color: darkgray;
padding: .5em;
border: solid 1px black;
}
.child {
background-color: lightgray;
padding: .5em;
border: solid 1px black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<parent></parent>
</div>
More Detailed explanation
There are couple of errors on your code that I'll go through.
In your child component
When initializing your component, this is not available inside the data function, so this.globalForm will be undefined. An error is thrown in the console when reproducing it.
data() {
return {
options: ["level1","level2","level3",], // this looks like duplicated code from the parent
level: this.globalForm.level // throws error
};
}
To fix that error, you can get the vm context from the parameters of data But this is not the solution for your question.
data(vm) { // note vm
return {
level: vm.globalForm.level // note vm
};
}
The real problem is that level: this.globalForm.level runs only once, in your component initialization, so level is undefined. When the globalForm prop changes, level has already been initialized and it will not change (data returns a new object so the reference to the prop is lost).
You want convert level to be a computed property so that changes to the prop can be detected and the inner value returned. See code snippet above.

Vuejs Es6 class reactivity

i'm trying to have a computed property in vuejs associated to a es6 class.
My Vue instance looks like this:
...
props: ['customClass'],
computed: {
localClass: {
get() {
return this.customClass
},
set (value) {
console.log("changed")
}
}
}
...
My class looks like this
class CustomClass {
constructor () {
this.selected = false
}
}
If i try to do something like that:
this.localClass.selected = true
but the setter is never called, like the reactivity has been lost and i don't understand why.
I also try:
Vue.set(this.localClass, 'selected', true)
I pass customClass as a prop, but even creating a new instance directly in the component it doesn't change the result.
In vuejs docs i don't recall a section talking about reactivity problem in es6 class, so i was wondering if someone know why and how to make my class reactive.
Thanks in advance
The setter of a computed property, say myComputedProperty, is triggered when you assign to that property (e.g. this.myComputedProperty = {something: 'else'}.
What you probably are looking for is a watcher, more specifically, a watcher with deep: true, such as:
watch: {
localClass: {
deep: true,
handler() {
out.innerHTML += "watched!";
}
}
},
Demo below.
class CustomClass {
constructor() {
this.selected = false
}
}
Vue.component('custom', {
template: '#custom',
props: ['customClass'],
computed: {
localClass: {
get() {
return this.customClass
},
set(value) {
out.innerHTML += "changed!\n";
}
}
},
watch: {
localClass: {
deep: true,
handler() {
out.innerHTML += "watched!\n";
}
}
},
methods: {
assignToSelected() {
this.localClass.selected = true
},
assignToLocalClass() {
this.localClass = {
selected: true
}
}
}
});
new Vue({
el: '#app',
data: {
test: new CustomClass()
},
})
#out { background: black; color: gray; }
span { font-size: x-small; font-family: verdana }
<script src="https://unpkg.com/vue"></script>
<template id="custom">
<div>
{{ localClass }}
<br>
<button #click="assignToSelected">assignToSelected</button>
<span>Note: will trigger "watched!" just once, because, since the value is hardcoded in the method (see code) subsequent clicks won't modify the value.</span>
<br><br>
<button #click="assignToLocalClass">assignToLocalClass</button>
<span>Note: assignToLocalClass() will trigger the computed setter, but wont trigger the watcher because the computed setter currently sets nothing, so nothing changed for the watcher to trigger.</span>
</div>
</template>
<div id="app">
<custom :custom-class="test"></custom>
</div>
<pre id="out"></pre>

How to solve [Vue warn]: Avoid mutating a prop directly since the value will be overwritten on vue.js 2?

My view is like this :
<div class="col-md-8">
...
<star :value="{{ $data['rating'] }}"></star>
...
</div>
My star component is like this :
<template>
<span class="rating" :class='{"disable-all-rating": !!value}'>
<template v-for="item in items">
<label class="radio-inline input-star" :class="{'is-selected': ((starValue>= item.value) && starValue!= null)}">
<input type="radio" class="input-rating" v-model="starValue" #click="rate(item.value)">
</label>
</template>
</span>
</template>
<script>
export default{
props: {
'value': null
},
computed: {
starValue () {
return this.temp_value
}
},
data(){
return{
items: [
{value: 5},
{value: 4},
{value: 3},
{value: 2},
{value: 1}
],
temp_value: null,
}
},
methods:{
rate: function (star) {
this.$http.post(window.BaseUrl + '/star', {star: star});
this.temp_value = star;
},
}
}
</script>
My css is like this :
span.rating {
direction: rtl;
display: inline-block;
}
span.rating .input-star {
background: url("../img/star.png") 0 -16px;
padding-left: 0;
margin-left: 0;
width: 16px;
height: 16px;
}
span.rating .input-star:hover, span.rating .input-star:hover ~ .input-star {
background-position: 0 0;
}
span.rating .is-selected{
background-position: 0 0;
}
span.rating .is-disabled{
cursor: default;
}
span.rating .input-star .input-rating {
display: none;
}
When I click the star, there exist error on the console like this :
[Vue warn]: Avoid mutating a prop directly since the value will be
overwritten whenever the parent component re-renders. Instead, use a
data or computed property based on the prop's value. Prop being
mutated: "value" (found in at
C:\xampp\htdocs\myshop\resources\assets\js\components\Star.vue)
How can I solve it?
You need to make the computed with a getter and a setter and then use $emit to update the prop e.g:
computed: {
starValue:{
get:function () {
return this.temp_value
},
set: function(newValue){
// $emit is the correct way to update props:
this.$emit('update:value', newValue);
}
}
}
You're changing the value property here.
return this.value = star;
And possibly here.
v-model="value"
The warning means that whenever your view is re-rendered, the value property is going to be set to $data['rating'], overwriting whatever you did inside the start component.
Instead of mutating the property inside your component, when someone clicks a star, you probably want to $emit that the component has changed and let your view change $data['rating'], which will re-render the star component properly.
See the Vue documentation regarding component composition.
The warning suggests not modifying a prop's value directly as doing so you would lose the change anyway if the data at the parent component changes.
That said, here in your method:
methods: {
rate: function (star) {
var self = this;
if (!this.disabled) {
this.$http.post(window.BaseUrl + '/star', {star: star}).then(function (response) {
console.log('submitted');
});
this.temp_value = star;
// return this.value = star; - remove this line
}
}
}
and add a computed property like so:
computed: {
starValue () {
return this.temp_value
}
}

Vuex input with v-model not reactive

I try to explain it as simple as possible. I have something like this. Simple Vue root, Vuex store and input with v-model inside navbar id.
That input is not reactive... Why?!
HTML
<div id="navbar">
<h2>#{{ test }}</h2>
<input v-model="test" />
</div>
store.js
import Vuex from 'vuex'
export const store = new Vuex.Store({
state: {
test: 'test'
},
getters: {
test (state) {
return state.test
}
}
})
Vue Root
import { store } from './app-store.js'
new Vue({
el: '#navbar',
store,
computed: {
test () {
return this.$store.getters.test
}
}
})
You're binding to a computed property. In order to set a value on a computed property you need to write get and set methods.
computed:{
test:{
get(){ return this.$store.getters.test; },
set( value ){ this.$store.commit("TEST_COMMIT", value );}
}
}
And in your store
mutations:{
TEST_COMMIT( state, payload ){
state.test=payload;
}
}
Now when you change the value of the input bound to test, it will trigger a commit to the store, which updates its state.
You don't want to use v-model for that. Instead, use #input="test" in your input field and in the your methods hook:
test(e){
this.$store.dispatch('setTest', e.target.value)
}
Inside your Vuex store:
In mutations:
setTest(state, payload){
state.test = payload
},
In actions:
setTest: (context,val) => {context.commit('setTest', val)},
The input should now be reactive and you should see the result in #{{test}}
Here is an example of how I handle user input with Vuex: http://codepen.io/anon/pen/gmROQq
You can easily use v-model with Vuex (with actions/mutations firing on each change) by using my library:
https://github.com/yarsky-tgz/vuex-dot
<template>
<form>
<input v-model="name"/>
<input v-model="email"/>
</form>
</template>
<script>
import { takeState } from 'vuex-dot';
export default {
computed: {
...takeState('user')
.expose(['name', 'email'])
.commit('editUser') // payload example: { name: 'Peter'}
.map()
}
}
</script>
The computed property is one-way. If you want two-way binding, make a setter as the other answers suggest or use the data property instead.
import { store } from './app-store.js'
new Vue({
el: '#navbar',
store,
// computed: {
// test() {
// return this.$store.getters.test;
// }
// }
data: function() {
return { test: this.$store.getters.test };
}
});
But a setter is better to validate input value.

Categories