1
votes

I would like to drag columns by their headers in a v-data-table. I have gotten pretty far using directives/sortablejs, but I have trouble figuring out the following:

  • I'm not sure how to account for when the number of <td> columns do not match the number of <th> columns. This can happen when colspan="someNumber" is used
  • When a new row is added it doesnt sync up to the correct columns when the columns are dragged to a new location.

Any help/insight would be appreciated!

I have a codepen of this setup at: https://codepen.io/uglyhobbitfeet/pen/NWWKVza

The most important parts of the codepen are:

<v-data-table v-sortable-table

and

directives: {
  'sortable-table': {
    ...
  }
}
2
The th does move when I drag it. I would imagine it provides you with dragging events that give you an opportunity to re-order your array?Dominic
Yes, the th moves but not the corresponding tds that go along with it (aka the entire column)ekjcfn3902039

2 Answers

4
votes

I went a slightly different route by using a key on the data-table and a custom onEnd method. Working code is here:

https://codepen.io/uglyhobbitfeet/pen/ExxaNGO

<v-data-table :headers="headers" :items="desserts"
  sort-by="calories" 
  v-sortable-table="{onEnd:sortTheHeadersAndUpdateTheKey}"
  :key="anIncreasingNumber" >
4
votes

I am putting the core details of ekjcfn3902039's implementation here so you don't have to sort through all of the other stuff going on in the great Pen. Full credit goes to him/her.

  1. Add SortableJS to your project (via a CDN or install via NMP is up to you.)
  2. Import SortableJS into your .Vue file.
  3. Add this function to the top of you <script> area:
    function watchClass(targetNode, classToWatch) {
      let lastClassState = targetNode.classList.contains(classToWatch);
      const observer = new MutationObserver((mutationsList) => {
        for (let i = 0; i < mutationsList.length; i++) {
          const mutation = mutationsList[i];
          if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
            const currentClassState = mutation.target.classList.contains(classToWatch);
            if (lastClassState !== currentClassState) {
              lastClassState = currentClassState;
              if (!currentClassState) {
                mutation.target.classList.add('sortHandle');
              }
            }
          }
        }
      });
      observer.observe(targetNode, { attributes: true });
    }
  1. Add these props to your v-data-table def:
    v-sortable-table="{onEnd:sortTheHeadersAndUpdateTheKey}"
    :key="anIncreasingNumber"
  1. Add this data variable: anIncreasingNumber: 1,
  2. Add the following Method and change this.tableHeaders to your headers object:
    sortTheHeadersAndUpdateTheKey(evt) {
      const headersTmp = this.tableHeaders;
      const oldIndex = evt.oldIndex;
      const newIndex = evt.newIndex;
      if (newIndex >= headersTmp.length) {
        let k = newIndex - headersTmp.length + 1;
        while (k--) {
          headersTmp.push(undefined);
        }
      }
      headersTmp.splice(newIndex, 0, headersTmp.splice(oldIndex, 1)[0]);
      this.table = headersTmp;
      this.anIncreasingNumber += 1;
    }
  1. Add this directive:
    directives: {
    'sortable-table': {
      inserted: (el, binding) => {
        el.querySelectorAll('th').forEach((draggableEl) => {
          // Need a class watcher because sorting v-data-table rows asc/desc removes the sortHandle class
          watchClass(draggableEl, 'sortHandle');
          draggableEl.classList.add('sortHandle');
        });
        Sortable.create(el.querySelector('tr'), binding.value ? { ...binding.value, handle: '.sortHandle' } : {});
      },
    },
  }

I have boiled it down to its bare minimum in this Pen: https://codepen.io/bradlymathews/pen/QWyXpZv?editors=1010