从 vuex 状态数组创建计算属性数组以用于 v-model
make array of computed properties from vuex state array for use in v-model
我在 Vue 3 中有以下设置。
将数组作为状态的一部分的 vuex 存储:
const store = createStore({
state: {
questions: [
{ text: 'A', value: false },
{ text: 'B', value: false },
{ text: 'C', value: true },
],
},
mutations: {
updateQuestionValue(state, { index, value }) {
state.questions[index].value = value;
},
},
});
还有一个组件,它尝试呈现一个复选框列表,该列表应对应于状态中的“问题”数组。
<template>
<div v-for="(question, index) in questions">
<label :for="'q'+index">{{question.text}}</label>
<input :id="'q'+index" v-model="questionComputeds[index]" type="checkbox" />
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const questions = computed(() => store.state.questions);
const questionComputeds = store.state.questions.map((q, i) =>
computed({
get() {
return store.state.questions[i].value;
},
set(value) {
store.commit('updateQuestionValue', { index: i, value });
},
})
);
</script>
如您所见,我希望使用 v-model 为列表中的每个输入元素执行两种方式的绑定,但是因为我将 vuex 与数组一起使用,所以我想使用 get/set我的计算属性上的选项,并使用模板中的索引访问特定的计算。但是我发现这不起作用。它不会引发错误,但它也无法将复选框的值绑定到我的问题对象中的 .value
道具。我对这里的策略完全不屑一顾吗?您甚至可以像我使用 .map()
一样制作“计算”数组吗?有什么方法可以使用这个数据结构的 v-model 吗?
第一个问题:
在模板的第一行
<div v-for="(question, index) in questions">
questions
未定义。它可以被替换为
<div v-for="(question, index) in questionComputeds">
或者在创建时传递给商店的 questions
可以提取到一个变量,在您的组件中导出和导入
第二个问题:
你的第二行少了一个 .value
应该是
<input v-model="questionComputeds[index].value" type="checkbox" />
这两个更改应该可以解决它。考虑到你不需要 v-for 中的索引,你得到
完整的解决方案:
<template>
<div v-for="question in questionComputeds">
<input v-model="question.value" type="checkbox" />
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const questionComputeds = store.state.questions.map((q, i) =>
computed({
get: () => store.state.questions[i].value,
set: (value) => { store.commit('updateQuestionValue', { index: i, value }) },
})
);
</script>
备选方案:
简单的解决方案:
<template>
<div v-for="(question, index) in questionComputeds">
<input :checked="questionComputeds[index]" type="checkbox" @input="handleChange(index, $event)" />
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const handleChange = (i, e) => {
store.commit('updateQuestionValue', { index: i, value: e.target.checked });
// to verify:
// console.log({ ...store.state.questions[i] });
}
const questionComputeds = computed(() => store.state.questions.map(q => q.value))
</script>
更复杂(但更可重用)的解决方案:
将复选框数组提取到单独的组件并定义自定义 v-model
我在 Vue 3 中有以下设置。
将数组作为状态的一部分的 vuex 存储:
const store = createStore({
state: {
questions: [
{ text: 'A', value: false },
{ text: 'B', value: false },
{ text: 'C', value: true },
],
},
mutations: {
updateQuestionValue(state, { index, value }) {
state.questions[index].value = value;
},
},
});
还有一个组件,它尝试呈现一个复选框列表,该列表应对应于状态中的“问题”数组。
<template>
<div v-for="(question, index) in questions">
<label :for="'q'+index">{{question.text}}</label>
<input :id="'q'+index" v-model="questionComputeds[index]" type="checkbox" />
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const questions = computed(() => store.state.questions);
const questionComputeds = store.state.questions.map((q, i) =>
computed({
get() {
return store.state.questions[i].value;
},
set(value) {
store.commit('updateQuestionValue', { index: i, value });
},
})
);
</script>
如您所见,我希望使用 v-model 为列表中的每个输入元素执行两种方式的绑定,但是因为我将 vuex 与数组一起使用,所以我想使用 get/set我的计算属性上的选项,并使用模板中的索引访问特定的计算。但是我发现这不起作用。它不会引发错误,但它也无法将复选框的值绑定到我的问题对象中的 .value
道具。我对这里的策略完全不屑一顾吗?您甚至可以像我使用 .map()
一样制作“计算”数组吗?有什么方法可以使用这个数据结构的 v-model 吗?
第一个问题:
在模板的第一行
<div v-for="(question, index) in questions">
questions
未定义。它可以被替换为
<div v-for="(question, index) in questionComputeds">
或者在创建时传递给商店的 questions
可以提取到一个变量,在您的组件中导出和导入
第二个问题:
你的第二行少了一个 .value
应该是
<input v-model="questionComputeds[index].value" type="checkbox" />
这两个更改应该可以解决它。考虑到你不需要 v-for 中的索引,你得到
完整的解决方案:
<template>
<div v-for="question in questionComputeds">
<input v-model="question.value" type="checkbox" />
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const questionComputeds = store.state.questions.map((q, i) =>
computed({
get: () => store.state.questions[i].value,
set: (value) => { store.commit('updateQuestionValue', { index: i, value }) },
})
);
</script>
备选方案:
简单的解决方案:
<template>
<div v-for="(question, index) in questionComputeds">
<input :checked="questionComputeds[index]" type="checkbox" @input="handleChange(index, $event)" />
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const handleChange = (i, e) => {
store.commit('updateQuestionValue', { index: i, value: e.target.checked });
// to verify:
// console.log({ ...store.state.questions[i] });
}
const questionComputeds = computed(() => store.state.questions.map(q => q.value))
</script>
更复杂(但更可重用)的解决方案:
将复选框数组提取到单独的组件并定义自定义 v-model