我想使用复选框根据其类别过滤一些产品。
为此,我有一个父组件,它将可能的类别(例如 A、B 和 C)传递给子组件并跟踪选中/选定的类别。
但是,棘手的部分是可能的类别是动态的:有时类别数组可能会缩小或增长。
由于复选框的布尔值不再对应于正确的位置,这会导致不正确的行为。例如,前两个复选框被选中,但每当数组更改时,前两个值仍然被选中(现在是不同的值)。此处演示了这一点:
我设法通过让子级使用本地属性跟踪复选框本身并在更新时发送给父级来解决这个问题。但是,如果我添加第二个子项,则子项 1 和子项 2 不再同步(例如,当我在移动菜单中使用 1 个过滤器,在普通菜单中使用 1 个过滤器时)。这显示在这里:
因此,这两种解决方案都不是最佳的,感谢您对这个难题的任何帮助!
下面是简化的代码。每当最低价格过滤器发生变化时,FilterOptions 数组仅显示满足价格标准的可能产品类别。发生这种情况时,位置不再对应于正确的值。
子组件 - FilterCheckboxItem.vue
<template>
<div
v-for="(FilterOption, index) in FilterOptions"
>
<label for="lname">{{FilterOption}}</label>
<input
type="checkbox"
:value="FilterOption"
v-model="modelValue[index]"
/>
</div>
</template>
<script>
export default {
props: {
FilterOptions: {
type: Array,
},
modelValue: {
type: Array,
},
},
}
</script>
父组件
<template>
<FilterCheckboxItem
ref="Category"
name="Category"
:FilterOptions="CategoriesWithinPriceReq"
v-model="FilteredCategories"
/>
<input v-model="MinimalPrice">
</template>
<script>
import FilterCheckboxItem from './Comp.vue'
export default {
components: { FilterCheckboxItem },
data(){
return {
FilteredCategories: [],
CategoryOptions: ["A","B","C"],
MinimalPrice: 5,
Products:
[
{name: 'test1', price: 10, category: "A"},
{name: 'test2', price: 15, category: "B"},
{name: 'test3', price: 20, category: "C"},
{name: 'test4', price: 8, category: "C"}
]
}
},
computed: {
FilteredProducts: function(){
return this.filterProductsByCategory(this.filterProductsByPrice(this.Products))
},
CategoriesWithinPriceReq: function(){
let CategoriesMeetingFilter = this.filterProductsByPrice(this.Products);
let uniqueCatgeoriesMeetingFilters = [
...new Set(CategoriesMeetingFilter.map(({ category }) => category)),
];
return uniqueCatgeoriesMeetingFilters;
},
},
methods: {
filterProductsByPrice: function(products){
return products.filter((product)=>product.price>=this.MinimalPrice)
},
filterProductsByCategory: function(products){
const selected_categories = this.CategoriesWithinPriceReq.filter((category, bool) => this.FilteredCategories[bool])
const results = products.filter((product) => selected_categories.indexOf(product.category) !==-1)
// Only filter if atleast one box is checked, otherwise return all products
return selected_categories && selected_categories.length? results: products
}
}
}
</script>
每个孩子通过
FilteredCategories
更新v-model
:
<div>Child 1</div>
<FilterCheckboxItem
:FilterOptions="CategoriesWithinPriceReq"
v-model="FilteredCategories"
/>
<div>Child 2</div>
<FilterCheckboxItem
:FilterOptions="CategoriesWithinPriceReq"
v-model="FilteredCategories"
/>
当其中一个组件更新变量时,另一个组件必须更新其复选框的选中状态。因此它必须对传入的变化做出反应。您可以通过观察者轻松完成此操作:
// FilterCheckboxItem.vue
setup(props, { emit }) {
...
watch(
() => props.modelValue,
() => checked.value = props.modelValue
)
...
},
这里是更新的游乐场
但是,这将导致子组件更新从父组件传递的数组(道具中的
modelValue
变为 checked
,由复选框更新)。这被认为是糟糕的形式。由于checked
上的观察者,不可能只使用modelValue
的副本(即checked.value = [...props.modelValue]
),因为它会导致无限循环(设置checked
会导致发射,从而更新modelValue
,再次设置 checked
)。
解决方法是仅在通过设置
@input
处理程序单击复选框时发出。由于您使用的是v-model
的复选框数组模式,因此必须等到数组更新后才能发出:
<input
type="checkbox"
:value="filterOption"
v-model="checked"
@input="emitOnNextCycle"
/>
和
setup(props, { emit }) {
const checked = ref([]);
const emitOnNextCycle = () => setTimeout(() => emit('update:modelValue', checked.value))
watch(
() => props.modelValue,
() => checked.value = [...props.modelValue]
)
return { checked, emitOnNextCycle };
},
参见游乐场
除了
setTimeout()
,您还可以使用 nextTick()
或 Promise.resolve()
,在这种情况下它们都执行相同的操作。
作为提示,请避免混合选项 API 元素和设置函数,这会造成混乱并导致不明确的行为。 此外,绝大多数 JS 程序员使用驼峰式命名法来命名变量,而 PascalCase 则保留用于类和类型。随你所欲。
希望有帮助。