computed property set not called in Vue
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 oflocalStorage
:
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
), settingdeep=true
andhandler
to a function that setslocalStorage
with the new value:
export default { watch: { options: { deep: true, handler(options) { localStorage.setItem('my-data', JSON.stringify(options)) } } },}
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>