Search code examples
javascriptvue.jsvuejs2rubaxa-sortable

Sortable.js with Vue 2.0 sorts incorrectly


I am using Sortable.js and Vue.js. The goal is to sort items and keep data updated.

It worked well with Vue 1.x, but after update to 2.0 sorting became incorrect. Array still properly updates, but items in DOM are in the wrong places.

new Vue({
  el: '#app',
  template: '#sort',
  data: function() {
    return {
      items: [
        "http://placehold.it/200X300?text=image1",
        "http://placehold.it/200X300?text=image2",
        "http://placehold.it/200X300?text=image3",
        "http://placehold.it/200X300?text=image4"
      ],  
    }
  },
  mounted: function() {
    this.$nextTick(function () {
      Sortable.create(document.getElementById('sortable'), {
        animation: 200,
        onUpdate: this.reorder.bind(this),
      });
    })
  },
  methods: {
    reorder: function(event) {
        var oldIndex = event.oldIndex,
            newIndex = event.newIndex;
        this.items.splice(newIndex, 0, this.items.splice(oldIndex, 1)[0]);

    } 
   }
});

jsFiddle https://jsfiddle.net/4bvtofdd/4/

Can someone help me?


Solution

  • As it happens, Sortable keeps track of the order in sortable.toArray(), so it's pretty easy to make a computed that will give you the items in sorted order, while the original item list is unchanged.

    new Vue({
      el: '#app',
      template: '#sort',
      data: {
        items: [
          "http://placehold.it/200X300?text=image1",
          "http://placehold.it/200X300?text=image2",
          "http://placehold.it/200X300?text=image3",
          "http://placehold.it/200X300?text=image4"
        ],
        order: null
      },
      computed: {
        sortedItems: function() {
          if (!this.order) {
          	return this.items;
          }
          return this.order.map((i) => this.items[i]);
        }
      },
      mounted: function() {
        this.$nextTick(() => {
          const sortable = Sortable.create(document.getElementById('sortable'), {
            animation: 200,
            onUpdate: () => { this.order = sortable.toArray(); }
          });
        })
      }
    });
    <script src="//cdnjs.cloudflare.com/ajax/libs/Sortable/1.4.2/Sortable.min.js"></script>
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="//unpkg.com/[email protected]/dist/vue.js"></script>
    <div id='app'></div>
    <template id="sort">
      <div class="container">
        <div class="row sort-wrap" id="sortable">
          <div class="col-xs-6 col-md-3 thumbnail" v-for="(item, index) in items" :data-id="index">
            <img v-bind:src="item" alt="" class="img-responsive">
          </div>
        </div>
        <div v-for="item in sortedItems">
        {{item}}
        </div>
      </div>
    </template>