我试图使拖放列表与Draggable插件,但取决于如何从组件(第二个代码块),负责嵌套它的工作只有一种方式在监视器中的新值
Codesandbox - https://codesandbox.io/s/bold-aj-jwvh93?file=/src/App.vue
主要部件列表
<script setup>
import { ref, watch, reactive, onMounted } from 'vue';
import DraggableListNesting from 'COMPONENT/cms/DraggableListNesting.vue';
const props = defineProps(
{
list:
{
type: Array,
required: true,
default: [],
},
});
const list = ref(props.list);
const emits = defineEmits(
[
'getChildrens',
'updateList',
'listUpdated',
]);
watch(() => props.list, (newList) =>
{
list.value = newList;
},
{
deep: true,
flush: 'post'
});
const childrensUpdated = (args) =>
{
for(let i = 0; i < list.value.length; i++)
{
if(list.value[i].id == args.parentElementId)
{
list.value[i].childrens = args.list;
list.value[i].childrens_count = args.list.length;
}
}
// list.value = Object.assign(args.list, list.value);
};
</script>
<template>
<div>
<DraggableListNesting
v-model="list"
:childrensUpdated="childrensUpdated"
@getChildrens="(args) => emits('getChildrens', args)"
@childrensUpdated="(args) => childrensUpdated(args)"
/>
</div>
</template>
字符串
嵌套组件- DraggableListNesting
<script setup>
import { ref, watch } from 'vue';
import Draggable from 'vuedraggable';
import DraggableItem from 'COMPONENT/cms/DraggableItem.vue';
import DraggableListNesting from 'COMPONENT/cms/DraggableListNesting.vue';
const props = defineProps(
{
modelValue:
{
type: Array,
required: true,
default: [],
},
parentId:
{
type: [Number, Boolean],
required: false,
default: false,
},
childrensUpdated:
{
type: Function,
required: true,
},
});
const emits = defineEmits(
[
'getChildrens',
'childrensUpdated',
]);
const list = ref(props.modelValue);
const parentId = ref(props.parentId);
watch(() => props.modelValue, (newList) =>
{
console.log('list before update by watch');
console.log(list.value);
console.log(newList);
list.value = newList;
// list.value = Object.assign(newList, list.value);
},
{
deep: true,
flush: 'post'
});
</script>
<template>
<Draggable
v-model="list"
tag="ul"
:item-key="item => item.id"
:group="{name: 'edit_list_draggable_nested'}"
@end="(...args) =>
{
emits('childrensUpdated', {list: list, parentElementId: parentId, depth: nestDepth});
}"
>
<template #item="{ element }" :key="element.id">
<div>
{{ element.name }}
</div>
<li :data-draggable-item-id="element.id">
<DraggableListNesting
v-model="element.childrens"
:parentId="element.id"
:childrensUpdated="childrensUpdated"
@getChildrens="(args) => emits('getChildrens', args)"
@childrensUpdated="(args) => childrensUpdated(args)"
></DraggableListNesting>
</li>
</template>
</Draggable>
</template>
型
列表属性中的一些示例数据
[
{
"id": 16,
"name": "Settings",
"parent_id": null,
"order": 16,
'childrens': [
{
"id": 18,
"name": "Logs",
"parent_id": 16,
"order": 18,
childrens: [],
"childrens_count": 1
},
{
"id": 17,
"name": "Backups",
"parent_id": 16,
"order": 17,
childrens: [],
"childrens_count": 0
}
],
"childrens_count": 2
},
{
"id": 12,
"name": "Analytics",
"parent_id": null,
"order": 12,
childrens: [],
"childrens_count": 0
},
]
型
因此,对于测试,我将“Backups”(“Settings”的子项)从beign子项拖到主列表中
如果我坚持使用“list.value = newList”
- “备份”正确地从“设置”移动到主列表,但当观察者运行自己的列表.值有正确的已经修改的列表,但newList得到的旧列表没有“备份”还没有
- 当我移动“分析”是一个其他项目的孩子它的所有罚款
如果我尝试执行“list.value = Object.assign(newList,list.value);”(注解掉),
- 将“Backups”从嵌套中移除到主列表中也没问题
- 但是,当我尝试嵌套“分析”作为另一个项目的孩子,它唯一的视觉添加到“设置”的孩子(例如),但“设置”对象在列表 prop 没有他作为孩子
移动成功后,我还需要发送Axios请求与父ID,所以列表值必须更新
2条答案
按热度按时间oogrdqng1#
我不知道我做了什么,我想我只是简化了你的代码。基本上,我认为bug的根本原因是你的代码不知何故没有正确更新v-model(比如你使用观察者来更新数据,而数据可能已经被v-model更新了,同时,v-model只更新组件内部的ref,但不更新组件的父组件内部的数据)。
我认为最重要的修复是这段代码:
字符串
以前,它使用v-model,但现在我使用v-model扩展(
:model-value
和@update:model-value="(newValue) => emitUpdate(newValue)"
)。然后,emitUpdate将数据Map到新数据。型
通过这种方式,我们可以在将数据传播到父组件之前对新数据做一些事情(我们可以分配新的
parentId
)。最后,当每个数据都更新时,我们可以用下面的代码来查看根父节点中的数据:
型
每当拖动事件发生时,上面的监视器就会被触发,我们可以安全地假设
list
ref是它的最新版本。这是分叉沙盒:
x1c 0d1x的数据
编辑
OP在评论中指出,它仍然有一个bug,渲染的项目与源数据不匹配。我不确定我做了什么,但主要是,我只是将
element
改为使用list[index]
,如下所示:型
此外,由于某些原因,使用此方法,固定parent_id会重复项目,因此我们可以使用此函数在根中递归地固定parent_id:
型
以下是分叉沙箱:
的
hsvhsicv2#
当你使用Object.assign(newList,list.value)时,它会将newList中的属性合并到list.value中,但不会用新对象替换list.value,这可能是Vue检测到更改所必需的。
在将newList赋值给list.value之前,可以考虑创建newList的深度副本。这可以使用JSON方法(JSON.parse(JSON.stringify(newList)))或深度副本实用函数来完成。这可以确保您使用的是一个新鲜的对象,这可以帮助Vue的React系统检测更改。
更新:
下面是代码的修改版本,实现了一些建议:
字符串