Reputation: 3887
Vuex is really nice to display complex data, and do complex mutations (like adding a node to a tree with repercussions all across the store etc.), However, to me, it feels like a pain in the ass dealing with "simple" mutations on complex objects.
One use case I'm facing is (quite) simple : I have a component that displays a list object (with nested properties and arrays). Each properties are editable. Each object is displayed with a subcomponent. (and I need to use v-model on leaf properties)
The other use case is a modal dialog that is opened using a component's method, that will permit to edit some properties / nested properties of the object (and also, I needs to use v-model on leaf properties)
Currently, This is what I do :
In the components that display / edit an object, I put in the component's data
a "skeleton" of the objects that could be edited (an object with all nested properties present, but all leaf set to null
). Then, when the modal open, or the component should display / make editable an object, I simply deep copy the object (using a recursive function) to the data
one, and I watch the data one with deep
option to true
, and commit on each change.
This feels a bit wrong, and needs quite an effort to make it work.
I like the mutation traceability system, and what I would like is to commit my whole object each time a modification is done on a nested property. That would be really great if it was possible without writing such an amount of code (I'm wondering if it wouldn't be worthy to simply give up vuex for a simple global Vue Componnent store... and trade off the nice mutation system for simple code)
So is there a "standard" way or a "recommended way" ...Or simply a "smart" way to do it without requiring such an effort ?
Example of store state :
state = {
foo : [
bar : {
users : [
{
name : '',
adddr : '',
needs : ['a', 'b', 'c'],
baz : [
{
k1 : v1,
k2 : v2,
k3 : [1,2,3,4,5],
},
//...
]
},
//...
]
}
]
}
...And I would like to commit each time whole user.
Upvotes: 16
Views: 10260
Reputation: 3887
Well... This problem pissed me off so hard that I wrote a module to handle all these complex cases. One can find it here : https://github.com/hl037/vue-data-proxy
Then, one could simply write this kind of componnent :
import vueDataProxy from "vue-data-proxy";
export default {
props : {
user_ind;
}
computed : {
...vueDataProxy({
user : {
fetch() {
return this.$store.state.foo.bar.users[this.user_ind];
},
commit(val){
// here, val is the full object (if reactivity injected)
// returned by fetch
this.$store.commit('updateUser', {ind : this.user_ind, val}
}
}
},
//...
}
...Supposing you have a mutation called updateUser(state, {ind, val}) { /*...*/ }
...Then, it will commit on each modification at any depth
Upvotes: 4
Reputation: 485
I suggest to use on this way:
Component
<template>
<input v-on:change="update(‘object.nested.more.nested.attribute’, $event)" v-text:value="object.nested.mote.nested.attribute"></span>
</template>
<script>
export default {
methods: {
update: function (property, value) {
this.$store.dispatch(UPDATE_COMPLEX_OBJECT, {property, value})
}
}
}
</script>
VUEX
import _ from 'lodash'
export default {
namespaced: true,
state: {
myObject: {}
},
mutations: {
[UPDATE_COMPLEX_OBJECT] (state, data) {
_.set(state.myObject, data.property, data.value)
}
},
actions: {
[UPDATE_COMPLEX_OBJECT]: ({commit}, data) => {
commit(UPDATE_COMPLEX_OBJECT, data)
}
}
}
Let me know if something is not clear and hope this help you. Thanks!
Upvotes: 7
Reputation: 562
Pretend you have a complex nested object like the following one:
{
super: {
nested: {
object: {
to: {
be: {
updated: 'foo'
}
}
}
}
}
}
Now, you may want to have an action which updates a specific key:
import * as types from '../mutation-types'
import * as defaultStore from '../defaultStore'
export default {
namespaced: true,
state: defaultStore.complexObject,
getters: {
getSettings: state => state
},
mutations: {
[types.UPDATE_SUPER_NESTED_OBJECT] (state, payload) {
state.super.nested.object.to.be.updated = payload
}
},
actions: {
updateSuperNestedObject (store, payload) {
return store.commit(types.UPDATE_SUPER_NESTED_OBJECT, payload)
}
}
}
In your component:
import { mapActions } from 'vuex'
export default {
computed: {
...mapActions('<modulename>', ['updateSuperNestedObject'])
}
methods: {
handleUpdate(payload) {
return this.updateSuperNestedObject('bar')
}
}
}
Hope it helps!
Upvotes: 0