javascriptvue.jssortablejsvue.draggable

Drag an element but drop an array of other elements


I'm using the Vue.Draggable library that is based on the SortableJS library.

In my code I have 2 draggable lists but I have a third one that is not incorporated with simple elements.

The third list has an array of object with an endpoint entry in each one, and when I drop my element I want to do a function that get an array of objects from the endpoint and merge it with the targgeted list.

var vm = new Vue({
  el: "#main",
  data: {
    list: [{
      name: "John"
    }, {
      name: "Joao"
    }, {
      name: "Jean"
    }],
    list2: [{
      name: "Juan"
    }, {
      name: "Edgard"
    }, {
      name: "Johnson"
    }],
    list3: [{
      name: "First",
      endpoint: 'http://api.com/first'
    }, {
      name: "Second",
      endpoint: 'http://api.com/second'
    }, {
      name: "Third",
      endpoint: 'http://api.com/third'
    }]
  },
  methods: {
    getAllTheFirstNames (evt) {
        // i want to do something like this
      axios.get(evt.elementDragged.endpoint)
        .then(res => {
            evt.dropList.add(res.data /* res.data is an array of names */)
        })
    },
  
    add: function() {
      this.list.push({
        name: 'Juan'
      });
    },
    replace: function() {
      this.list = [{
        name: 'Edgard'
      }]
    }
  }
});
.normal {
  background-color: grey;
}

.drag {
  background-color: green;
}

.dragArea {
  min-height: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.7.0/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.17.0/vuedraggable.min.js"></script>
<div id="main">
  <h1>Vue Draggable</h1>           
  <div class="drag">
    <h2>List 1 Draggable</h2>
    <draggable v-model="list" class="dragArea" :options="{group:'people'}">
      <div v-for="element in list">{{element.name}}</div>
    </draggable>

    <h2>List 2 Draggable</h2>
    <draggable v-model="list2" class="dragArea" :options="{group:'people'}">
      <div v-for="element in list2">{{element.name}}</div>
    </draggable>

    <h2>List 3 Draggable</h2>
    <draggable v-model="list3" class="dragArea" :options="{group:'people'}" @end="getAllTheFirstNames">
      <div v-for="element in list3">{{element.name}}</div>
    </draggable>
  </div>
</div>

Here's the fiddle


Solution

  • For those who are going to have a similar problem, here's the solution.

    First you need to work with the @end event, then in your method you can get the id of the dragged element by accessing an attribut that you'll initialize in your v-for, in my case it was data-id so I could access it like so:

    let id = evt.item.dataset.id
    

    Now that you have your id in your function you can get your array of objects with your API.

    Next, you need to add the array that you just recovered into the second list with the right index, to do this, you need to get the targeted index of the second list and feed it with your array:

    let index = evt.newIndex
    this.secondList.splice(index, 0, ...myArray)
    

    Last step, you need to clean the second list because the initial dragged element will be there too, for me I just did a loop and if I didn't find a certain attribut in one of the objects it means that it must be removed, but you can think of anything really.

    Here's the whole code:

    <draggable
        v-model="firstList"
        :clone="clone"
        :options="options"
        @end="onEnd">
        <div
            v-for="c in firstList"
            :key="c.id"
            :data-id="c.id">
        </div>
    </draggable>
    
    ...
    
    onEnd (evt) {
        const id = evt.item.dataset.id
        HTTP.get(`/${id}/getMyArray`)
            .then(res => {
                let myObjects = JSON.parse(res.data) // => [{...}, {...}, ...]
                let index = evt.newIndex
                this.secondList.splice(index, 0, ...myObjects)
            })
    }