Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 631
Jack Barham Avatar asked Feb 08 '23 23:02

Jack Barham


2 Answers

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.

like image 83
Peter Avatar answered Feb 11 '23 01:02

Peter


I created a Vue.js ic directive to handle this kind of update in a generic way. It can be used exactly as v-for directive but add the drag and drop capacity and corresponding updates on the view model array.

Usage:

<div v-dragable-for="element in list">{{element.name}}</div>

Demo:

Example.

Fiddle: example 1, example 2

GitHub: Vue.Dragable.For

like image 45
David Desmaisons Avatar answered Feb 10 '23 23:02

David Desmaisons