Stack Overflow Asked by LuMa on February 12, 2021
I have used VueJS and Vuex before. Not in production, just for some simple, personal side projects and it felt pretty straight forward.
However, now I face a problem that is just slightly more complex but using Vuex already feels way more complicated. So I’m looking for some guidance here.
In the main view I present a list of cards to the user. A card is an editable form of about ten properties (inputs, selects, etc.). I obviously implemented a component for these cards, since they are repeated in a list view. My first naive approach was to fetch a list of – let’s call it forms – from my store and then using v-for
to present a card for each form in that list of forms. The form is passed to the child component as a property.
Now I want to bind my form controls to the properties. To do this I implemented Two-way Computed Properties to properly utilize my store mutations. But it feels extremely repetitive to implement a custom computed property with getter and setter plus mutation for every property on my model. Additionally, the forms are an array in my state. So I have to pass the id of the form to edit in the store to every mutation.
Something else I had in mind was just passing the store id of the form to the child component and have "by id" getters for every property of my model as well as a matching mutation. But this doesn’t feel like the proper way to do this, either. It is essentially the same, right?!
Is there a better solution to this problem? Maybe I’m just missing something or I’m overcomplicating things.
A trimmed down example:
Editor.vue
:
<template>
<v-container>
<EditableCard v-for="(card, i) in cards" :key="i" :card="card" />
</v-container>
</template>
<script>
import EditableCard from "@/components/EditableCard";
import { mapGetters } from "vuex";
export default {
name: "Editor",
components: {
EditableCard
},
computed: {
...mapGetters("cards", {
cards: "list"
})
}
};
</script>
EditableCard
:
<template>
<v-card>
<v-form>
<v-card-title>
<v-text-field v-model="title"></v-text-field>
</v-card-title>
<v-card-text>
<v-text-fieldv-model="text"></v-text-field>
<!-- And some more fields... -->
</v-card-text>
</v-form>
</v-card>
</template>
<script>
import { mapMutations } from "vuex";
export default {
name: "EditableCard",
props: {
card: Object
},
computed: {
title: {
get() {
return card.title;
},
set(value) {
this.setCardTitle(this.card.id, value);
}
},
text: {
get() {
return card.text;
},
set(value) {
this.setCardText(this.card.id, value);
}
}
// Repeat for every form input control
},
methods: {
...mapMutations("cards", {
setCardTitle: "setTitle",
setCardText: "setText"
// Repeat for every form input control
})
}
};
</script>
It would be nice to create a computed setter for the whole form object using a clone, but this won't work because changes won't trigger the computed setter.
If anyone wants to explore this interesting failure, see here)
To work around this, you can use a watch and a data clone:
<v-form>
<v-text-field v-model="clone.title" />
<v-text-field v-model="clone.text" />
</v-form>
props: ['index', 'card'],
data() {
return {
clone: {}
}
},
watch: {
card: {
handler(card) {
this.clone = { ...card }
},
immediate: true,
deep: true
},
clone: {
handler(n,o) {
if (n === o) {
this.$store.commit('SET_CARD', { index: this.index, card: n })
}
},
deep: true
}
}
Your v-for
:
<EditableCard v-for="(card, index) in cards" :card="card" :index="index" :key="index" />
The mutation:
mutations: {
SET_CARD(state, { index, card }) {
Vue.set(state.cards, index, card);
}
}
This is way more complex than it should need to be... but it works.
Answered by Dan on February 12, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP