v-sortable, a Vuejs directive for Sortable

This snippet provide a simple way to use rubaxa's Sortable javascript library with Vuejs.

Sortable allows you to easily transform elements into draggable elements (div, li, tr, ...). Apply the directive to the element that contains the elements you want to sort.

DEPRECATED

Lots have change in Vuejs. The first version of this directive was built with Vue 1. With Vue 2, directives have a less important role. In fact, that should be a component. I've updated the directive to work with Vue 2 (mainly to keep this demo working) but this is not the recommended way to do stuff. Have a look at Vue Draggable for a better approach.

Demo

  • Jim
  • Ray
  • Robby
  • John

That's the code for the demo:

<template>
  <div id="v-sortable-demo">
    <ul
      class="one-half"
      v-sortable="{
        options: {
          chosenClass: 'Musician--chosen',
          dataIdAttr: 'data-id',
          animation: 150,
        },
        handle: this.orderChanged
      }"
      >

      <li class="Musician" v-for="musician in band" :data-id="musician.name">
        {{ musician.name }}
      </li>

    </ul>

    <p v-if="speaks.length > 0">
      The list was reordered:
      <ul>
        <li v-for="spoken in speaks">orderChanged: {{ spoken }}</li>
      </ul>
    </p>
  </div>
</template>

<script>
import Vue from 'vue'
import sortable from './v-sortable'

Vue.directive('sortable', sortable)

export default {
  data() {
    return {
      band: [
        { name: "Jim" },
        { name: 'Ray' },
        { name: 'Robby' },
        { name: 'John' }
      ],
      speaks: []
    }
  },

  methods: {
    orderChanged(order) {
      this.speaks.push(order)
    }
  }
}
</script>

Usage

To use the directive on a component, you need to register it first.

// Load the directive
var sortable = require('./v-sortable');

// Register the directive globally
Vue.directive('sortable', sortable);

// Your component
var MyComponent = Vue.extend({
  template: '<div v-sortable :options="list"><div v-for="item in list"></div></div>',

  data: function () {
    return {
      list: [
        { id: 1 },
        { id: 2 },
        { id: 3 }
      ];
    }
  }
});

You can use the v-sortable directive on the element that contains the element you want to sort. For example:

<ul v-sortable>
  <li>Element 1</li>
  <li>Element 2</li>
  <li>Element 3</li>
</ul>

<div v-sortable>
  <div>Element 1</div>
  <div>Element 2</div>
  <div>Element 3</div>
</div>

<table v-sortable>
  <tr>Element 1</tr>
  <tr>Element 2</tr>
  <tr>Element 3</tr>
</table>

Pass any options defined on Sortable's documentation, and a named event. Then listen to the named event on your Vue instance and do whatever is appropriate.

For example, here we pass a chosenClass of 'Musician--chosen', which means that the dragged element will have this class. The dataIdAttr is how we want Sortable to identify the rows. In this example, it is the musician.name value, but it could be a musician.id value, or anything else. Then we tell the directive that our <custom-component> will be listening to an OrderChanged event.

<custom-component>
  <ul
    v-sortable="{
      options="{
        chosenClass: 'Musician--chosen',
        dataIdAttr: 'data-id',
        animation: 150
      },
      handle: this.orderChanged
    }"
    >
      <li
        v-for="musician in band"
        :data-id="musician.name"
        >
        {{musician.name}}
      </li>
  </ul>
<custom-component>

v-sortable.js

import Sortable from 'sortablejs'

let sortable

export default {
  bind(el, binding) {
    const options = {
      ...binding.value.options,
      onUpdate: function() {
        binding.value.handle(this.toArray())
      }
    }

    sortable = Sortable.create(el, options)
  },

  unbind() {
    sortable.destroy()
  }
}

Final note

This is not necessarily "the good way" to do it, and it will likely evolve. There might be a lot of things to do to improve this, and if you have any suggestions I would be happy to hear them and update this snippet accordingly.

You can check the source code of this page on this github repository.

Published about 3 years ago