Jack Barham Jack Barham - 5 months ago 129
Javascript Question

Resetting a Vue.js list order of all items after drag and drop

I have a drag and drop component (using Sortable) but I can't figure out the logic to update the list order once the the item is dropped into it's new location.

Code (Vue.js):

new Vue({
el: '#app',
template: '#dragdrop',
data() {
return {
list: [
{name: 'Item 1', id: 1, order: 0},
{name: 'Item 2', id: 2, order: 1},
{name: 'Item 3', id: 3, order: 2},
{name: 'Item 4', id: 4, order: 3},
{name: 'Item 5', id: 5, order: 4},
{name: 'Item 6', id: 5, order: 5},
],
}
},
ready() {
Sortable.create(document.getElementById('sort'), {
draggable: 'li.sort-item',
ghostClass: "sort-ghost",
animation: 80,
onUpdate: function(evt) {
console.log('dropped (Sortable)');
}
});
},
methods: {
dropped() {
console.log('dropped (Vue method)');
}
}
});


I have a working JSFiddle: https://jsfiddle.net/jackbarham/rubagbc5

I'm looking to get the
order
in the array to sync up so I can make an AJAX update once the item is dropped.

Answer

This is not the most elegant solution but I think it works. This uses the Sortable onUpdate handler to update the underlying array. Whenever an item is dragged to a new position, it gets moved to the same location in the array - this way the view model stays in sync with what's displayed in the view. Then the item order properties get updated to match their new positions in the array.

new Vue({
  el: '#app',
  template: '#dragdrop',
  data() {
    return {
      list: [
        {name: 'Item 1', id: 1, order: 0}, 
        {name: 'Item 2', id: 2, order: 1}, 
        {name: 'Item 3', id: 3, order: 2}, 
        {name: 'Item 4', id: 4, order: 3}, 
        {name: 'Item 5', id: 5, order: 4}, 
        {name: 'Item 6', id: 6, order: 5}, 
       ],
    }
  },
  ready() {
    var vm = this;
    Sortable.create(document.getElementById('sort'), {
      draggable: 'li.sort-item',
      ghostClass: "sort-ghost",
      animation: 80,
      onUpdate: function(evt) {
        console.log('dropped (Sortable)');
        vm.reorder(evt.oldIndex, evt.newIndex);
      }
    });
  },
  methods: {
    reorder(oldIndex, newIndex) {
      // move the item in the underlying array
      this.list.splice(newIndex, 0, this.list.splice(oldIndex, 1)[0]);
      // update order properties based on position in array
      this.list.forEach(function(item, index){
        item.order = index;
      });
    }
  }
});

You can optimize the reorder() method if you need to.

Here's an updated version of your jsfiddle.

I feel like this is the kind of functionality that one should try to package up into a custom directive but I haven't figured out exactly how to do that yet.