Vue.draggable is very useful UI component. It is a nice tool to create draggable items and represents nice sort animations. But sometimes you might want to use a swappable component instead of sortable one. If so this article will help you perhaps.
Why do I need to swap instead of sorting?
I want to store the sorted date persistence. I will store it to the database, but the data structure is not I wanted. the sort methods move several array index. for example, if I drag the first index element to last index element. The result is the following.
// sort
Before: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
After: [2, 3, 4, 5, 6, 7, 8, 9, 10, 1]
Otherwise, my expected result is the following.
// swap
Before: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
After: [10, 2, 3, 4, 5, 6, 7, 8, 9, 1]
Maybe, the expected result depends on the database structure, so I recommend to consult with server-side engineers if you will use Vue.draggable and store the order to your database.
Vue.draggable doesn't have swap feature
One day, some guy created an issue to ask the maintainer to add swap function to Vue.draggable. But the answer was NO.
swap feature #466
Actually, Sortable.js has swap plugin but current Vue.draggable version v2.21.0
doesn't have a swap feature in fact.
Hack the Vue.draggable
Anyway, I implemented an admin tool with Vue.draggable to swap the item orders, but I notice it reordered items and the data structure was not expected by a server-side engineer. So I hacked the component as the following codes.
<template>
<table>
<draggable v-model="items" tag="tbody" :move="handleMove" @end="handleDragEnd" :options="{animation:500}">
<tr class="movable" v-for="item in items" :key="item.id">
<td>{{ item.id }}</td><td>{{ item.name }}</td><td>{{ item.age }}</td>
</tr>
</draggable>
</table>
</template>
<script>
import draggable from "vuedraggable";
export default {
components: {
draggable,
},
data() {
const items = [
{ id: 1, name: "Bianka Effertz", age: 37 },
{ id: 2, name: "Mckayla Bogan", age: 20 },
{ id: 3, name: "Estevan Mann", age: 17 },
{ id: 4, name: "Cloyd Ziemann", age: 55 }
]
return { items }
},
methods: {
handleDragEnd() {
this.$toast.show('dragEnd')
this.futureItem = this.items[this.futureIndex]
this.movingItem = this.items[this.movingIndex]
const _items = Object.assign([], this.items)
_items[this.futureIndex] = this.movingItem
_items[this.movingIndex] = this.futureItem
this.items = _items
},
handleMove(e) {
const { index, futureIndex } = e.draggedContext
this.movingIndex = index
this.futureIndex = futureIndex
return false // disable sort
}
}
</script>
demo is here
Let describe the codes
Vue.draggable has move
property through the argument path to Sortable.js. It has move event fired when the item is moved. so we can pass the handler function for the event. And the event is required to return false, -1 or 1. It means as the following.
- return false; — for cancel
- return -1; — insert before target
- return 1; — insert after target
So I change return false every the onMove event fired, as a result, the sorting function is disabled but I can catch the dragged event context contains target index and future Index. Now we have known to requirement data, so we can implement the Vue.draggable to be swappable.
recap
By some chance, we have more smart methods Vue.draggable to be swappable. But It's the one way If you can't find the solution.