Vue3嵌套vue-draggable时子组件无法获取父级属性解决方案问询
问题根因
你拿不到父级属性有两个核心原因:
vue-draggable的item插槽只会向外暴露内置的几个上下文参数:当前循环项element、项索引index,你给<draggable>标签绑定的自定义属性(比如你写的mainType)不会自动注入到插槽作用域,自然解构不出来。- 你内外两层
#item插槽都用element当解构变量名,内层的同名变量直接遮蔽了外层作用域中代表品牌对象的element,本来不需要传值就能直接访问到父级属性,被重名变量盖住了。
实现方案
方案1:直接利用模板作用域(最简单,适配你当前的两层嵌套场景)
内层draggable本身就写在外层品牌列表的插槽内部,外层插槽解构出的品牌对象在整个内层作用域都可以直接访问,只需要把内外层的解构变量重命名避免冲突就行,不需要额外做属性传递。
核心修正代码如下:
<!-- 外层品牌列表:把解构出的品牌项重命名为brandItem,避免和内层重名 --> <draggable :list="brand" :disabled="!enabled" item-key="name" class="list-group" ghost-class="ghost" v-bind="dragOptions" handle=".handle" :group="{ name: 'brand' }" @start="dragging = true" @end="dragging = false"> <template #item="{ element: brandItem, index: brandIndex }"> <div class="list-group-item" :class="{'not-draggable' : !enabled}"> <div class="row handle drag-object"> ID: {{ brandItem.id }} , Name: {{ brandItem.name }} <button class="btn btn-primary" type="button" data-bs-toggle="collapse" :data-bs-target="'#target'+brandItem.id" aria-expanded="false" aria-controls="collapseExample"> Details </button> <div class="row"> <div class="collapse" :id="'#target'+brandItem.id" :class="{'show' : showAll}"> <div class="card card-body size-classes"> <!-- 不需要给内层draggable传自定义mainType --> <draggable :list="brandItem.content" :disabled="!enabled2" item-key="size-class" class="list-group" ghost-class="ghost" v-bind="dragOptions" handle=".handle" :group="{ name: 'size-class'+brandItem.name}" @start="dragging2 = true" <!-- 拖拽事件直接把当前品牌对象当参数传入 --> @end="handleSizeSort(brandItem, $event)"> <!-- 内层尺码项重命名为sizeItem,避免和外层brandItem重名 --> <template #item="{ element: sizeItem, index: sizeIndex }"> <div class="list-group-item size-classes-bar" :class="{'not-draggable' : !enabled2}"> <div class="row handle drag-object"> <!-- 直接访问外层的brandItem就能拿到父级品牌属性 --> 所属品牌: {{ brandItem.name }} ,尺码ID: {{ sizeItem.id }} , 尺码名: {{ sizeItem.name }} <button class="btn btn-primary" type="button" data-bs-toggle="collapse" :data-bs-target="'#target2'+sizeItem.id" aria-expanded="false" aria-controls="collapseExample"> Details </button> </div> <div class="collapse" :id="'#target2'+sizeItem.id" :class="{'show' : showAll2}"> <div class="card card-body size-classes-details"> <h1>Something ..</h1> </div> </div> </div> </template> </draggable> </div> </div> </div> </div> </div> </template> </draggable>
对应的拖拽排序处理函数示例:
const handleSizeSort = (currentBrand, dragEvent) => { dragging2.value = false // 这里直接就能拿到当前操作的品牌id、name console.log('当前修改的品牌:', currentBrand.id, currentBrand.name) // currentBrand.content就是排序后的尺码列表,直接做后续逻辑即可 }
方案2:抽离内层组件(适合复杂业务、嵌套层级≥3的场景)
如果后续拖拽逻辑会变复杂,可以把内层尺码拖拽区域抽成独立组件,通过props显式传入当前品牌对象,逻辑解耦更易维护:
- 新建
SizeDraggable.vue组件:
<template> <draggable :list="currentBrand.content" :disabled="!enabled2" item-key="size-class" class="list-group" ghost-class="ghost" v-bind="dragOptions" handle=".handle" :group="{ name: 'size-class'+currentBrand.name}" @start="dragging2 = true" @end="handleDragEnd"> <template #item="{ element: sizeItem, index: sizeIndex }"> <div class="list-group-item size-classes-bar" :class="{'not-draggable' : !enabled2}"> <div class="row handle drag-object"> 所属品牌: {{ currentBrand.name }} ,尺码ID: {{ sizeItem.id }} , 尺码名: {{ sizeItem.name }} <!-- 省略其他原有结构 --> </div> </div> </template> </draggable> </template> <script setup> const props = defineProps({ currentBrand: { type: Object, required: true } }) const handleDragEnd = () => { dragging2.value = false // 直接通过props.currentBrand访问父级品牌属性 console.log('当前操作品牌:', props.currentBrand.name) } </script>
- 外层直接引入组件传值即可:
<template #item="{ element: brandItem }"> <!-- 省略其他原有结构 --> <SizeDraggable :current-brand="brandItem" /> </template>
内容的提问来源于stack exchange,提问作者Wong Yat Cheong Jacky
相关产品推荐
相关产品推荐

