【发布时间】:2019-12-21 08:42:28
【问题描述】:
我正在尝试设置一个 Vue 组件,该组件采用数组中的项目的平面列表,按属性对它们进行分组以在子组件中使用,并发出更新的平面数组。
我的部分组件在其 v-model 中使用这些分组的项目并发出更新的列表。部分组件是带有一些输入字段的拖放,因此在部分组件下更改项目并发出更新的列表。
这是一个将平面列表作为道具的组件示例:
<template>
<div>
<div v-for="section in template.sections" :key="section.id">
<h2>{{ section.name }}</h2>
<item-section :section="section" v-model="sectionData[section.id]"></item-section>
</div>
</div>
</template>
<script type="text/javascript">
import { groupBy } from "lodash";
import ItemSection from "@/components/Section.vue";
export default {
name: "ItemAssignment",
props: {
// All items in flat array
value: {
type: Array,
required: true,
default: () => [
/**
* {
* id: null,
* section_id: null,
* name: null
* }
*/
]
},
// Template (containing available sections)
template: {
type: Object,
default: () => {
return {
sections: [
/**
* {
* id: null,
* name: null
* }
*/
]
};
}
}
},
components: {
ItemSection
},
data() {
return {
sectionData: []
};
},
mounted() {},
computed: {
flattenedData() {
return Object.values(this.sectionData).flat();
}
},
methods: {},
watch: {
// Flat list updated
value: {
immediate: true,
deep: true,
handler(val) {
this.sectionData = groupBy(val, "section_id");
}
},
// --- Causing infinite loop ---
// flattenedData(val) {
// this.$emit("input", val);
// },
}
};
</script>
这个组件的父级基本上是这样的:
<template>
<div>
<!-- List items should be updatable here or from within the assignment component -->
<item-assignment v-model="listItems"></item-assignment>
</div>
</template>
<script type="text/javascript">
import ItemAssignment from "@/components/ItemAssignment.vue";
export default {
name: "ItemExample",
props: {
},
components: {
ItemAssignment
},
data() {
return {
listItems: []
};
},
mounted() {},
computed: {
},
methods: {
// Coming from API...
importExisting(list) {
var newList = [];
list.forEach(item => {
const newItem = {
id: null, // New record, so don't inherit ID
section_id: item.section_id,
name: item.name
};
newList.push(newItem);
});
this.listItems = newList;
}
},
watch: {
}
};
</script>
当发出最终的平面数组时,Vue 进入一个无限循环,试图重新处理列表并且浏览器选项卡冻结。
我相信 groupBy 和/或 Object.values(array).flat() 方法正在剥离反应性,因此 Vue 一直认为它是不同的数据,因此是无限循环。
我尝试手动循环遍历这些项目并将它们推送到一个临时数组,但遇到了同样的问题。
如果有人知道如何在保持反应性的同时对这些项目进行分组和展平,我将不胜感激。谢谢!
【问题讨论】:
-
你试过将 v-model 分解为 value 和 @input 吗?因为这基本上就是 v-model 的组成部分。
-
是的。尝试将 :value 和 @input 用于进行展平的方法,但是一旦在此组件中发出新的展平数组,就会出现相同的问题。你的意思是在这个的父组件上尝试这个?
-
所以我想你可以将 item-section 的值绑定到一个计算属性(这是你的平面数组的分组版本)。在 item-section 的输入上,使用一种方法来更新平面数组。这样你就可以删除平面数组上的手表了
-
是导致循环的
$emit还是父组件的监听器?可以分享一下父组件对ItemAssignment的使用吗? -
感谢您的建议。如果我使用计算设置器,它不会在更新时设置。我似乎需要一个深度观察者来检测重新排序和项目值更新。每当我向这个组件传递一个新列表时,我都会得到恒定的输入/输出循环。