How to Use VueDraggable With BootstrapVue's b-table
Introduction
Most of the time, we use multiple libraries and packages in our project and stumble upon a problem that has limited examples or the existing ones that don’t work for our specific scenario, and there’s a lack of information about using VueDraggable with Bootstrap Vue’s b-table
.
By the end of this blog post, we are going to learn how to use VueDraggable with Bootstrap Vue’s b-table
, and update the model as we made changes in the DOM.
Before we dive into the solution let’s go over existing ones and examine why they don’t work as expected.
Examining Existing Solutions
With a quick search, you might come across this GitHub issue about Bootstrap Vue’s integration with VueDraggable, which leads to this StackOverflow question then also leads to this jsfiddle example
The missing ingredient with this solution is that the data model is not reacting to the changes made in the DOM. It also uses Sortable.js library as a solution, which falls short on updating data model.
Check out this example to see it in action.
So, let’s take a closer look at why the data model is not affected.
SortableJS approach ( and why it doesn’t trigger data model to change )
By using SortableJS we can achieve the drag and drop functionality on a DOM level. But, since SortableJS uses pure JavaScript to manually manipulate the DOM, Vue isn’t aware of these changes, hence the data model doesn’t update.
template
<div id="app">
<b-table v-sortable="sortableOptions" striped hover :items="items" class="table-item"></b-table>
<div class="items">
<p class="model-header">Data Model</p>
<pre>
{{ formattedItems }}
</pre
>
</div>
</div>
script
const createSortable = (el, options, vnode) => {
return Sortable.create(el, {
...options
});
};
const sortable = {
name: "sortable",
bind(el, binding, vnode) {
const table = el;
table._sortable = createSortable(table.querySelector("tbody"), binding.value, vnode);
}
};
var app = new Vue({
el: "#app",
directives: {
sortable
},
data() {
return {
items: [
{
id: 1,
name: "Abby",
sport: "basketball"
},
{
id: 2,
name: "Brooke",
sport: "soccer"
},
{
id: 3,
name: "Courtenay",
sport: "volleyball"
},
{
id: 4,
name: "David",
sport: "rugby"
}
],
sortableOptions: {
chosenClass: "is-selected"
}
};
},
computed: {
formattedItems() {
const str = JSON.stringify(this.items, null, 2);
return str;
}
}
});
As we can see, when we drag and drop elements, the data model stays the same. Which is not what we want as a result.
Now, let’s move on to the next solution to make it work.
Using VueDraggable ( and why it triggers data model to change )
VueDraggable uses Sortable.js underneath, but since it is written for the Vue, our data model and the DOM always stays in sync.
Let’s see how it works with an example. ( Just a heads up, we are using VueJS2 in this example, for Vue3 take a look at VueDraggableNext )
template
<div id="app">
<b-table-simple class="table-item">
<b-thead>
<!-- You can dynamically generate column names
or manually create it -->
<!-- Dynamic column Names -->
<!-- <b-th v-for="(item, name) in items[0]" :key="name" >
{{ name }}
</b-th> -->
<!-- Static column Names -->
<b-th> Id </b-th>
<b-th> Name </b-th>
<b-th> Sport </b-th>
</b-thead>
<!-- Draggable rows -->
<draggable :class="{ [`cursor-grabbing`]: drag === true }" v-model="items" group="items" @start="drag = true" @end="drag = false" tag="tbody">
<b-tr v-for="item in items" :key="item.id" class="item-row">
<b-td> {{ item.id }}</b-td>
<b-td>{{ item.name }}</b-td>
<b-td>{{ item.sport }}</b-td>
</b-tr>
</draggable>
</b-table-simple>
<!-- Display data model -->
<div class="items">
<p class="model-header">Data Model</p>
<pre>
{{ formattedItems }}
</pre
>
</div>
</div>
import draggable from "vuedraggable";
export default {
name: "App",
components: {
draggable
},
data() {
return {
drag: false,
items: [
{ id: 1, name: "Abby", sport: "basketball" },
{ id: 2, name: "Brooke", sport: "soccer" },
{ id: 3, name: "Courtenay", sport: "volleyball" },
{ id: 4, name: "David", sport: "rugby" }
]
};
},
computed: {
// Format data type to be displayed as JSON string
formattedItems() {
const str = JSON.stringify(this.items, null, 2);
return str;
}
}
};
As expected, when the position of the rows has changed, our data model gets updated synchronously.
At this point, you might ask, why are we using b-table-simple
instead of b-table
component, because currently, there’s no workaround to directly integrate VueDraggable with b-table.
You can check out this GitHub issue for a detailed explanation.
Conclusion and live examples
Next time you use BootstrapVue with VueDraggable remember:
If you’re using VueJS2 stick to VueDraggable, otherwise ( meaning using Vue3 ) use VueDraggableNext.
Use
b-table-simple
instead ofb-table
component.
Check out this live example and use it as a template next time you integrate b-table-simple component with VueDraggable.
You can also take a look at this Sortable.js demo to compare it with the working solution.
If you want me to create an example with Vue3 and VueDraggableNext shoot me a DM , share your thoughts and ideas. I’m listening!
#bootstrap-vue #b-table #b-table-simple #vuejs-2 #sortablejs #vue-draggable #vue-draggable-next