Search code examples
vue.jsvuejs3vuedraggable

Vue Draggable in Draggable and how to get parent's element


i am using vue3 and using vue-draggable.

i had a array:

brand: [
{ id: 0, order: 0 ,name: "Adidas", content:[
        {id: 0, order: 0, name:"UK Shoe Size"},
        {id: 1, order: 1, name:"US Shoe Size"},
    ]},
{ id: 1, order: 1 ,name: "Fila",  content: [
        {id: 0, order: 0, name:"US Shoe Size"}
    ]},
{ id: 2, order: 2 ,name: "K-Swiss" , content: [
        {id: 0, order: 0, name:"UK Shoe Size"},
        {id: 1, order: 1, name:"US Shoe Size"},
        {id: 2, order: 2, name:"Eur Shoe Size"}
    ]},]

and i show it with in 2 draggable components. view of the page

It works on looping the brand, and brand.content. But i cannot got the brand.name or brand.id in child (Draggable in brand.content), like if i drag and drop in Adidas (brand.id = 0), i cannot know which content i changed. I cannot access the brand.id from the child draggable.

I tried to use v-bind, but it not working.

Any suggestions? Thanks for helping!

Here is the simplify code:

<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, index }">
    <div class="list-group-item" :class="{'not-draggable' : !enabled}">
      <div class="row handle drag-object">
        ID: {{ element.id }} , Name: {{ element.name }}

        <button class="btn btn-primary" type="button" data-bs-toggle="collapse" :data-bs-target="'#target'+element.id"
          aria-expanded="false" aria-controls="collapseExample">
          Details
        </button>

        <div class="row">
          <div class="collapse" :id="'target'+element.id" :class="{'show' : showAll}">
            <div class="card card-body size-classes">

              <!--    v-bind:mainType not working    -->
              <draggable v-bind:mainType="element.name" :list="element.content" :disabled="!enabled2"
                item-key="size-class" class="list-group" ghost-class="ghost" v-bind="dragOptions" handle=".handle"
                :group="{ name: 'size-class'+element.name}" @start="dragging2 = true" @end="dragging2 = false">

                <!--   v-bind:mainType not working    -->
                <template #item="{ element, index, mainType }">
                  <div class="list-group-item size-classes-bar" :class="{'not-draggable' : !enabled2}">
                    <div class="row handle drag-object">

                      <!--  cannot get the mainType   -->
                      inform: {{ mainType }} ,ID: {{ element.id }} , Name: {{ element.name }}

                      <button class="btn btn-primary" type="button" data-bs-toggle="collapse"
                        :data-bs-target="'#target2'+element.id" aria-expanded="false" aria-controls="collapseExample">
                        Details
                      </button>
                    </div>

                    <div class="collapse" :id="'target2'+element.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>

Solution

  • Simple solution: in the content item scope, you have access to two variables named element. They are shadowing each other, so it picks the "closest" one.

    Just rename it in the destructuring, and you could refer to both variables:

    <draggable>
      <template #item="{ element: brand, brandIndex }"> <!-- Rename element to brand -->
        ID: {{ brand.id }} , Name: {{ brand.name }}
        <draggable>
          <template #item="{ element: content, index: contentIndex }"> <!-- Rename element to content -->
            {{ brand.id }} {{ content.id }} <!-- You have access to both variables -->
          </template>
        </draggable>
      </template>
    </draggable>
    

    A better solution would be to simplify your component by creating sub components, and pass the brand / content as a prop:

    <!-- Parent component -->
    <draggable>
      <template #item="{ element, index }">
        <brand-component :brand="element" :index="index />
      </template>
    </draggable>
    
    <!-- Brand component -->
    <draggable>
      <template #item="{ element, index }">
         <sub-component :brand="brand" :content="element" :index="index" />
      </template>
    </draggable>
    
    
    props: {
      element: Object,
      index: Number,
    }
    
    <!-- Brand Content component -->
    <button>Whatever</button>
    
    props: {
      brand: Object,
      element: Object,
      index: Number,
    }
    

    Notes:

    • mainType prop doesn't seem to exist on draggable component.
    • props should be written in kebab-case in templates.