How to add drag&drop functionality to BootstrapVue DataTable columns

5/17/2021

Vue.js BootstrapVue

For the company web application i'm currently working on, we were asked to implement a drag&drop functionality on the columns of a data table, so that the final user can rearrange the column order (we're taling about 15/16 columns, so a pretty large table).

I've found several working examples, but none of them was using the bootstrapvue's complete data-table but only custom implementations of the simple data table.

Let's start right now. Set up a new bootstrapvue project: you can find instructions on how to do that directly on their docs.

Once we've done that, let's quickly remove everything from the HelloWorld.vue file that was auto-generated when starting the new project.

<template>
</template>
<script>
export default {
}
</script>

Ok, now let's copy/paste the b-table example from bootstrapvue docs inside our template, and while we're at it, let's also add fields data, we'll need it later:

<template>
  <div>
    <b-table striped hover :fields="fields" :items="items"></b-table>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        fields: [
            'age', 'first_name', 'last_name'
        ],
        items: [
          { age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
          { age: 21, first_name: 'Larsen', last_name: 'Shaw' },
          { age: 89, first_name: 'Geneva', last_name: 'Wilson' },
          { age: 38, first_name: 'Jami', last_name: 'Carney' }
        ]
      }
    }
  }
</script>

We should now see something similar to this (remember to run npm run serve to launch the hot preview script and run the development in localhost:8080) image

Ok, so now comes the hard part.. Basically what we want is to be able to drag the header of a certain column and drop it somewhere else in order to change the columns order. BootstrapVue offers a template to easily customize the headers of the table but if we were to use this approach we'll lose some functionality we may need (example: sorting) and we would need to write a lot of code.

What i came up with is to add a table row above the headers, put a "grip" icon inside of it so it can be clear that it will be used to drag the column and change the css a bit in order to make it like it's the same cell as the header.

Let's proceed.

First, drag functionality is very easy to implement thanks to the awesome library vue-draggable (github).

Install the package with

npm i -S vuedraggable

Import it inside our component:

<script>
import draggable from "vuedraggable";
  export default {
    components: {
      draggable
    },
    data() {
      return {
        fields: [
            'age', 'first_name', 'last_name'
        ],
        items: [
          { age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
          { age: 21, first_name: 'Larsen', last_name: 'Shaw' },
          { age: 89, first_name: 'Geneva', last_name: 'Wilson' },
          { age: 38, first_name: 'Jami', last_name: 'Carney' }
        ]
      }
    }
  }
</script>

Now since we want to add a row above the headers, we'll use the thead-top template for that, as provided by bootstrapvue. Add these lines inside of the element:

<!--draggable columns-->
  <template #thead-top="">
    <draggable tag="b-tr" v-model="fields">
      <b-th v-for="(field, i) in fields" :key="i" class="draggable text-center">
        <b-icon-grip-horizontal font-scale="1.5"/>
      </b-th>
    </draggable>
  </template>

At this point, we should have it already working. Try dragging & dropping the grip icons to the left and right!

ezgif com-gif-maker

You can now customize this component by yourself, or if you're interested let's see how to "fake" the header cells to be the same!

<style>
.table td, th {
  border-width: 1px;
  border-style: solid;
  border-top: none;
  border-bottom-width: 0;
}
.table thead th:not(.draggable) {
  padding-top: 0;
  border-top-width: 0;
}
.table thead th.draggable {
  padding-bottom: 0;
  padding-top: 0;
  border-bottom-width: 0;
}
th svg {
  color: grey;
  cursor: ew-resize;
}
</style>

Now the table should look like this

image


You can find the github repository here: here

Was it useful?

Buy me a coffee :)