vue.draggable.next icon indicating copy to clipboard operation
vue.draggable.next copied to clipboard

Can not create nested lists

Open camohub opened this issue 2 years ago • 3 comments

Hi, I try to use you library with my Vue3 project. Simple example works fine. Dragging sorting events works fine on first level. But I can not insert items into another item. Only if item already have an nested list. Also it weirdly blink when I am inserting inside the nested list. So conclusion is I am not able to make new nested lists only if nested list already exists in item. Also I need to implement open/close arrow. Hope it is possible.

Here is my code. I created a component which recusrively call itself.

<template>
    <div class="category-table">
        <draggable :list="items" item-key="id" @change="logList()" :group="{name: 'g1'}">
            <template #item="{ element }">
                <div class="category">
                    <div class="category-title">
                        <div class="category-title__text">
                            <div class="drag-drop">
                                <Iconbase name="DragDrop"></Iconbase>
                            </div>
                            {{ element[titleKey] }}
                        </div>
                        <div class="category-title__buttons">
                            <div v-if="element[nestedItemsKey] && element[nestedItemsKey].length"
                                @click="toggleNestedItems(element[idKey])"
                                :class="{ active: activeId === element[idKey], arrow: true }"
                            >
                                <Iconbase name="Arrowdown" height="10" width="10"></Iconbase>
                            </div>
                            <div class="edit">
                                <EditActions :id="element[idKey]" :options="actions" @editactions="action($event)" />
                            </div>
                        </div>
                    </div>
                    <div class="divider-container" v-if="activeId === element[idKey]">
                        <hr class="divider" />
                    </div>
                    <NestedDraggable v-if="element[nestedItemsKey]"
                                     :items="element[nestedItemsKey]"
                                     :titleKey="titleKey"
                                     :nestedItemsKey="nestedItemsKey"
                                     @action="action($event)"
                    ></NestedDraggable>
                </div>
            </template>
        </draggable>
    </div>
</template>

<script lang="ts" setup>
import draggable from 'vuedraggable'
import EditActions from '@/components/ui/button/EditActions.vue'
import Iconbase from '@/components/icons/Iconbase.vue'
import { ref, defineProps, computed } from 'vue'

// Component props
const props = defineProps({
    items: {
        type: Array,
        required: true
    },
    // This is universal component which should handle any items object with its own keys.
    // Keys which component will use to access items object properties.
    // Component uses this: id, title, nestedItemsKey
    idKey: {
        type: String,
        default: 'id',
    },
    titleKey: {  // The item.key which will be used as title
        type: String,
        default: 'title',
    },
    nestedItemsKey: {  // The item.key where nested items are stored eg. item.children or item.subcategory
        type: String,
        default: 'subcategory'
    },
})

// Component emits (events)
const emit = defineEmits(['action'])

// EditActions component options
const actions = [
    { name: 'create_nested_item', ico: 'Pluscircle' },
    { name: 'edit', ico: 'Penciledit' },
    { name: 'delete', ico: 'Delete' }
]

let activeId = ref(-1)

function logList() {
    console.log('change')
    console.log(props.items)
}

function action(event) {
    console.log('NestedDraggable action')
    console.log(event)
    emit('action', event)
}

function toggleNestedItems(id) {
    console.log('toggleOpen')
    console.log(id)
    activeId.value = activeId.value === id
        ? -1   // Zatvorí sa aktívny accordion
        : id  // Otvorí sa vybraný accordion
}

</script>

camohub avatar Oct 05 '23 07:10 camohub

Hi @camohub,

I'm facing the same issue. Were you able to find a solution?

Thanks!

AZisdev avatar Feb 24 '25 14:02 AZisdev

Hi, finaly I used this library https://github.com/SortableJS/Vue.Draggable

camohub avatar Feb 24 '25 18:02 camohub

The code look like this

<template>
    <div class="category-table">
        <draggable v-model="data" item-key="id" @change="logList()">
            <template #item="{ element }">
                <div class="category">
                    <div class="category-title">
                        <div class="category-title__text">
                            <div class="drag-drop">
                                <Iconbase name="DragDrop"></Iconbase>
                            </div>
                            {{ element[titleKey] }}
                        </div>
                        <div class="category-title__buttons">
                            <div v-if="element[nestedItemsKey] && element[nestedItemsKey].length"
                                @click="toggleNestedItems(element[idKey])"
                                :class="{ active: activeId === element[idKey], arrow: true }"
                            >
                                <Iconbase name="Arrowdown" height="10" width="10"></Iconbase>
                            </div>
                            <div class="edit">
                                <EditActions :id="element[idKey]" :options="actions" @editactions="action($event)" />
                            </div>
                        </div>
                    </div>
                    <div class="divider-container" v-if="activeId === element[idKey]">
                        <hr class="divider" />
                    </div>
                    <div v-if="activeId === element[idKey] && element[nestedItemsKey]">
                        <NestedDraggable :items="element[nestedItemsKey]"
                                         :titleKey="titleKey"
                                         :nestedItemsKey="nestedItemsKey"
                                         @action="action($event)"
                        ></NestedDraggable>
                    </div>
                </div>
            </template>
        </draggable>
    </div>
</template>

<script lang="ts" setup>
import draggable from 'vuedraggable'
import EditActions from '@/components/ui/button/EditActions.vue'
import Iconbase from '@/components/icons/Iconbase.vue'
import { ref, defineProps, computed } from 'vue'

// Component props
const props = defineProps({
    items: {
        type: Array,
        required: true
    },
    // This is universal component which should handle any items object with its own keys.
    // Keys which component will use to access items object properties.
    // Component uses this: id, title, description, nestedItemsKey
    idKey: {
        type: String,
        default: 'id',
    },
    titleKey: {
        type: String,
        default: 'title',
    },
    nestedItemsKey: {
        type: String,
        default: 'subcategory'
    },
    translateKeys: []
})

// Component emits (events)
const emit = defineEmits(['action'])

// EditActions component options
const actions = [
    { name: 'create_nested_item', ico: 'Pluscircle' },
    { name: 'edit', ico: 'Penciledit' },
    { name: 'delete', ico: 'Delete' }
]

let data = ref(props.items)
let activeId = ref(-1)

const title = computed(() => {
    const organizations = settingsApi.value?.organizations ? settingsApi.value.organizations : [];

    if( !organizations.length ) return []

    let result = [{ title: 'all', value: -1 }];  // Result with init value

    organizations.forEach((item) => result.push({'title': item.name, 'value': item.id}))

    return result
})

function logList() {
    console.log('change')
    console.log(data.value)
}

function action(event) {
    console.log('NestedDraggable action')
    console.log(event)
    emit('action', event)
}

function toggleNestedItems(id) {
    console.log('toggleOpen')
    console.log(id)
    activeId.value = activeId.value === id
        ? -1   // Zatvorí sa aktívny accordion
        : id  // Otvorí sa vybraný accordion
}

</script>

camohub avatar Feb 24 '25 18:02 camohub