具有可拖动功能的 Vue3 - 嵌套列表 - 反应性

问题描述 投票:0回答:1

我尝试使用 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
    },
]

因此,为了测试,我将“备份”(“设置”的子项)从子项拖到主列表中

那么如果我坚持“list.value = newList”

  • “备份”已正确从“设置”移动到主列表中,但是当观察者运行自身时 list.value 具有正确的已修改列表,但 newList 获取没有“备份”的旧列表
  • 当我将“分析”移动为其他项目的子项时,一切都很好

否则,如果我尝试执行“list.value = Object.assign(newList, list.value);” (已注释掉)

  • 将“备份”从嵌套到主列表中取消嵌套就可以了
  • 但是当我尝试将“Analytics”嵌套为另一个项目的子项时,它唯一的视觉效果添加为“Setting”的子项(例如),但列表道具中的“Settings”对象没有将他作为子项

成功移动后,还需要发送带有父ID的axios请求,因此列表值必须更新

vue.js vuejs3 draggable vue-composition-api vuedraggable
1个回答
0
投票

当您使用 Object.assign(newList, list.value) 时,它将 newList 中的属性合并到 list.value 中,但它不会用新对象替换 list.value,这可能是 Vue 将其检测为所需的改变。

考虑在将 newList 分配给 list.value 之前创建其深层副本。这可以使用 JSON 方法 (JSON.parse(JSON.stringify(newList))) 或深层复制实用程序函数来完成。这确保您正在使用新的对象,这可以帮助 Vue 的反应系统检测更改。

© www.soinside.com 2019 - 2024. All rights reserved.