Vue.jsでドラッグ&ドロップのソートを実装する

こんにちは。コバヤシです。

今回はVue.jsでドラッグ&ドロップでソートする方法について書きたいと思います。

Vue.Draggableをインストールする

Vueでドラッグ&ドロップをするならこれっていうくらい定番のライブラリです。

github.com

まずこれをインストールします。

npm i -S vuedraggable

draggableを使う

draggableをコンポーネントと使用し、 ソートしたい箇所を以下のように「draggable」タグで囲みます。

<template>
  <div>
    <draggable v-model="list">
      <div v-for="val in list" :key="val.id">
        {{ val.name }}
      </div>
    </draggable>
  </div>
</template>


<script>
  const draggable = require('vuedraggable');
  export default {
    components: {
      draggable: draggable
    },
    data() {
      return {
        list: [
          {id:1, name:'hoge'},
          {id:2, name:'foo'},
          {id:3, name:'baa'},
        ]
      }
    }
  }
</script>

これだけでソートが出来るようになります。非常に簡単ですね。

ソート開始時、ソート完了時に処理を行う

draggableタグに@startを使うことでドラッグ開始時に行う処理を指定できます。 @endで完了時です。

ソート完了時にaxiosでバックエンド側にデータを送れば、ソート順を保存できます。

<template>
  <div>
    <draggable v-model="list" @start="start" @end="end">
      <div v-for="val in list" :key="val.id">
        {{ val.name }}
      </div>
    </draggable>
  </div>
</template>


<script>
  const draggable = require('vuedraggable');
  export default {
    components: {
      draggable: draggable
    },
    data() {
      return {
        list: [
          {id:1, name:'hoge'},
          {id:2, name:'foo'},
          {id:3, name:'baa'},
        ]
      }
    },
    methods: {
      start: () => {
        console.log('start')
      },
      end: () => {
        console.log('end')
      }
    }
  }
</script>

ドラッグする要素を指定する

そのままでは、draggableで囲んだ要素全体がドラッグ可能な要素となります。 handleを指定することで要素を変更することが出来ます。 下記では■の部分をクリックしたときのみドラッグが開始されます。

<template>
  <div>
    <draggable v-model="list" handle=".handle">
      <div v-for="val in list" :key="val.id">
        <span class="handle">■</span>{{ val.name }}
      </div>
    </draggable>
  </div>
</template>


<script>
  const draggable = require('vuedraggable');
  export default {
    components: {
      draggable: draggable
    },
    data() {
      return {
        list: [
          {id:1, name:'hoge'},
          {id:2, name:'foo'},
          {id:3, name:'baa'},
        ]
      }
    }
  }
</script>

タグを変更する

デフォルトではdraggableタグはdivタグになります。
これだとtableでtrをドラッグしたい時など困ります。 そこで以下のようにタグを指定します。

<template>
  <div>
    <table>
    <draggable tag="tbody" v-model="list" >
      <tr v-for="val in list" :key="val.id">
        <td>{{ val.name }}</td>
      </tr>
    </draggable>
    </table>
  </div>
</template>

IE11で動かない問題

Vue.Draggableをインストールしただけでは残念ながら動きません。 以下のようなエラーが表示されます。

[Vue warn]: Error in nextTick: "TypeError: Invalid attempt to spread non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method."

IEではSymbol.iteratorが使えないということのようです。。さすがIE。

対応するために今回はPolyfill.ioを使用することにしました。

polyfill.io

取り敢えず、
・Array.from
・Symbol.iterator

を選択して生成されたURLを読み込んだところIE11で無事動かすことが出来ました。

<script src="https://polyfill.io/v3/polyfill.min.js?features=Symbol.iterator%2CArray.from"></script>

まとめ

Vue.Draggableを使えば簡単にソートを機能を実現できました。
設定オプションも色々あるので、様々な場面で柔軟に対応できそうです。