TLDR At the bottom of the article you will find a link to the GitHub repo.
Prologue
Recently I have had to implement draggable rows for a vuetify table and right off the bat started searching in the docs and the webs for a solution. And came across this article written by Abhay Wawale and some other answers on StackOverflow & CodePen.
The article is great, but I was concerned about manipulating the DOM directly. As for me it is a bit hacky.
My Solution
Instead of accessing the item
slot in v-data-table
I decided to mess around with the body
slot. Basically body
slot is your <tbody>
html tag and it is very nice since we need a tag
prop for vuedraggable component which is a wrapper for SortableJS.
<v-data-table>
<template v-slot:body="props">
<draggable
:list="props.items"
tag="tbody"
>
<!-- the row will go here -->
</draggable>
</template>
</v-data-table>
Now to the rows, you just have to loop through the array of items that you pass into your v-data-table
items
prop. Those items will be exposed in the body
slot and can be accessed as follows:
<tr
v-for="(item, index) in props.items"
:key="index"
>
<td> {{ item.your-property-here }} </td>
</tr>
Let's add some actual data and make our table more fancy:
<v-data-table
:headers="tableHeaders"
:items="tableItems"
:loading="loading"
item-key="id"
:show-select="false"
:disable-pagination="true"
:hide-default-footer="true"
class="page__table"
>
<template v-slot:body="props">
<draggable
:list="props.items"
tag="tbody"
>
<tr
v-for="(user, index) in props.items"
:key="index"
>
<td>
<v-icon
small
class="page__grab-icon"
>
mdi-arrow-all
</v-icon>
</td>
<td> {{ index + 1 }} </td>
<td> {{ user.id }} </td>
<td> {{ user.name }} </td>
<td> {{ user.username }} </td>
<td> {{ user.email }} </td>
<td> {{ user.website }} </td>
<td>
<v-icon
small
@click="editUser(user.id)"
>
mdi-pencil
</v-icon>
</td>
</tr>
</draggable>
</template>
</v-data-table>
Our table will look like this and draggable rows are working!
Thanks for reading!
Discussion
This post just saved my bacon on an important project. I had been back and forth on many alternatives, none of which was the combination needed, until I saw this article. Having the full source code example, especially PageTable.vue, along with your working example was instrumental and so extremely helpful. Very much appreciated that you took the time to document this solution here.
I'm so glad that I could help you. Thanks for kind words!
Very interesting post. One question though: how to handle a 'drop'? What is triggered when you actually 'drop' something. If I make the list in a computed property with a getter and setter, no change is detected as the :list option using splice is used. When using the draggable :value option, it is not working...
So, how to handle it 'after the drop'?
A solution that works for me is adding the event-handler
@end="endDrag(props.items)"
in the element
Thanks, great!
Thanks so much! This was extremely helpful! I didn't think Vue Draggable was possible on Vuetify's Table Component until I found this.