I'm a little confused on how computed properties work with Vuex. I'm using a computed getter:
var selectDisplayValues = computed({
get() {
return store.getters['expense/getSelectDisplayValues'];
}
});
When the store data changes the computed prop also changes. So far so clear.
When now assigning a new value to the computed property - the value inside the store also changes. Not just the local value of the property. Why is that so? Won't I need a setter inside the computed prop to do so?
EDIT:
I'm assigning the new values like this.
selectDisplayValues.value[`inputData[${props.index}][${props.attribute}]`] = {placeholder_value: "Bitte wählen...", value: "", reassigned: false};
Also I'm using a v-model on a select dropdown for changing them according to the options value.
A new value wasn't assigned but existing value was mutated.
Getter-only computed ref has read-only value property that contains unmodified value.
If the intention is to make ref value deeply read-only, this needs to be explicitly done:
const selectDisplayValues = readonly(toRef(store.getters, 'expense/getSelectDisplayValues'))
I would personaly recommand using mapGetters from vuex : the mapgetters helper
I works like this :
You decalre a getter un your vuex store :
const store = createStore({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos (state) {
return state.todos.filter(todo => todo.done)
}
}
})
And in your vue component :
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
...mapGetters({
doneCount: 'doneTodosCount'
})
}
}
You can then access value from your getter with this.doneCount
If the store change the computed value wil automatically change
Related
I'm having trouble understanding how the code from a tutorial works.
I have the EventComponent component, which displays information about the event.
It uses a computed property which accesses Vuex store.
<h4>This event is {{ getEvent(1) }}.</h4>
export default {
computed: {
getEvent() {
return this.$store.getters.getEventById
}
}}
And this is my Vuex index.js file:
export default createStore({
state: {
events: [{id: 1, title: "Last day"}]
},
mutations: {},
getters: {
getEventById: state => id => {
return state.events.find(event => event.id === id)
}
},
actions: {},
modules: {}
})
The event info is displayed correctly. However, I'm confused by
How the computed property is able to accept an argument
How that argument is passed to the store getter, when the getter is not explicitly called with that argument
Could you help me understand this?
How the computed property is able to accept an argument
Since getEvent just returns this.$store.getters.getEventById (which is a getter for a function), getEvent also returns a function, and can therefore be called the exact same way.
How that argument is passed to the store getter, when the getter is not explicitly called with that argument
Actually, the getter is indeed being called with that argument. As mentioned above getEvent is effectively an alias for this.$store.getters.getEventById, so getEvent(1) is the same as this.$store.getters.getEventById(1).
Return a function from the computed property that takes the id as parameter:
<h4>This event is {{ getEvent(1) }}.</h4>
export default {
computed: {
getEvent() {
return (id)=>this.$store.getters.getEventById(id)
}
}}
I have this getter that get the value of one item in the state:
boxdata: state => {
return state.boxchart.data
},
Now I have another item in state that I use to change the value of getter
Currently I do this when component is mounted but it seems that the data sometimes loads but sometimes does not:
computed: {
...mapGetters(["boxdata"]),
...mapState(['reference_fc'])
},
mounted() {
this.boxdata[0].chartOptions.series[0].data[0]=this.reference_fc.NSR.values
}
So I wonder how can I ensure that the boxdata getter is already updated on the first time that the component loads?
Vue cannot detect array element assignments. This is explained in Caveats.
Try this instead:
this.$set(this.boxdata[0].chartOptions.series[0].data, 0, this.reference_fc.NSR.values)
You shouldn't mutate data using getters. You should use mutations.
Getters are only to get derived state based on store state. see here
In your store :
mutations: {
setBoxdata(state, value) {
state.boxchart.data[0].chartOptions.series[0].data[0] = value;
}
}
In your component :
computed: {
...mapMutations("setBoxdata")
},
mounted() {
this.setBoxData(this.reference_fc.NSR.values);
}
What is the correct syntax/hooks to make this work for myVal?
My code looks like this:
<v-item-group v-model="myVal" ...
import { mapActions, mapGetters } from 'vuex';
export default {
computed : {
...mapActions({
myVal: 'myModulePath/setMyVal'
}),
...mapGetters({
myVal: 'myModulePath/getMyVal'
}),
},
}
The store looks like:
actions: {
setMyVal({commit}, value){commit('someMutation',value);}
getters: {
getMyVal: state => { return state.myVal;}
I'm not sure how to wire it so the 'setter' works and the error message goes away.
I've also tried this to no avail:
...mapState('myModulePath', ['myVal'])
You need to define a single computed with a get and a set function. Maybe:
export default {
computed : {
myVal: {
get() { return this.$store.getters.getMyVal; },
set(newValue) { this.$store.dispatch('setMyVal', newValue); }
}
},
}
You need to tell the vue component what to do when the computed property is assigned a new value
computed: {
myVal: {
get: () => this.$state.store.getters.myModulePath.getMyVal,
set: (value) => this.$state.commit('someMutation', value )
}
}
Note that I use the setter instead of the action. Using an action in the computed property setter is a bad idea because actions are usually asynchronous and can cause headaches trying to debug the computed property later.
How can I watch for store values changes when params are used? I normally would do that via a getter, but my getter accepts a param which makes it tricky as I've failed to find documentation on this scenario or a stack Q/A.
(code is minimized for demo reasons)
My store.js :
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
let report = {
results: [],
};
export const store = new Vuex.Store({
state: {
broken: Object.assign({}, report),
},
results: (state) => (scan) => {
return state[scan].results
},
});
vue-component.vue :
computed: {
...mapGetters([
'results',
]),
watch: {
results(){ // How to pass the param ??
// my callback
}
So basically I would like to find out how to pass the param so my watch would work.
In my opinion, there is no direct solution for your question.
At first, for watch function, it only accept two parameters, newValue and oldValue, so there is no way to pass your scan parameter.
Also, your results property in computed, just return a function, if you watch the function, it will never be triggered.
I suggest you just change the getters from nested function to simple function.
But if you really want to do in this way, you should create a bridge computed properties
computed: {
...mapGetters([
'results',
]),
scan() {
},
mutatedResults() {
return this.results(this.scan);
},
watch: {
mutatedResults() {
}
}
}
If I have a Vuex module which is namespaced, how to create the getters and setters for the states in that module when using these state in Vue components?
// My component
new Vue({
computed: {
// How do I add setters also below????
...mapState('nameSpacedModA', {
a : state => state.a,
// ...
},
// Following will only add getters..
// How to add setter ???
...mapGetters('nameSpacedModA', {
a: 'a',
b: 'b' //, ...
}
}
I am binding 'a' to a text input on a form using v-model and then when I edit the control value, Vue is giving error:
[Vue warn]: Computed property "a" was assigned to but it has no
setter.
How to solve this?
If you want do do 2 ways binding, you need to defined both getter and setter in computed properties. (Don't forget to define mutation updateA)
<input v-model="a">
// ...
computed: {
a: {
get () {
return this.$store.state.a
},
set (value) {
this.$store.commit('updateA', value)
}
}
}
Another option is using mapFields
I found another way using Vuex mapStates and mapActions helpers.
This is slightly more verbose. So using the v-model binding approach is more better.
// BTW: If you use the approach as suggested by ittus then you will use the v-model binding like below:
<input v-model="a" />
// Using the other approach that I used you will have to do two-way binding as below:
<input :value="a" #input="updateA" />
If you want to use the v-model binding then the code will be something like below:
// Vuex store
....
modules: {ModuleA, ...}
// ModuleA of store
export default {
namespaced: true,
states: {
a: '',
},
mutations: {
updateA: (state, value) => state.a = value
},
actions: {
updateA(context, value) { context.commit('updateA', value) }
}
}
// Then in your Component you will bind this Vuex module state as below
new Vue({
store,
computed: {
a: {
get() { this.$store.state.ModuleA.a; }
set(value) {this.updateA(value);}
},
},
methods: {
...mapActions('MyModule', [ updateA ]),
}
})