忘れないように記録しとこ

カバの樹

[Vue3対応]Vue.jsでドラッグ&ドロップするなら「Vue.Draggable」がおすすめ

2022年12月5日

Vue.Draggableとは

Vue.Draggableは、Vue.jsをベースとしたドラッグ&ドロップコンポーネントライブラリです。
Vue.jsのアーキテクチャに基づいて設計されており、ドラッグ&ドロップの機能を簡単かつ効果的に実装できます。

コンパイルを必要としないUMD用のJSファイルが提供されており、jQueryからの切り替えも容易に行うことができます。
また、単純なドラッグ&ドロップの機能から、複数のエリアを移動することも可能で、スマートフォンにも対応しています。
タッチイベントによる移動も可能となっており、モバイルフレンドリーなWebアプリケーションを構築することができます。

総じて、Vue.Draggableは、Vue.jsの生産性を高め、開発効率を向上させるための素晴らしいツールであり、特にドラッグ&ドロップの実装において優れた機能性と使いやすさを提供します。
Webアプリケーションの開発において、Vue.Draggableを利用することで、柔軟なUI/UXを実現することができます。

 

【動画サイズ:87KB】

 

環境

この記事は、以下の管理人の検証環境にて記事にしています。

vue.js 3.2.26
Vue.Draggable 4.0.2

 

Vue2対応

Vue2を使用する場合は下記のURL
https://www.kabanoki.net/1712/

 

ライブラリの取得

ライブラリを取得するには、npm, yarn, CDNのどれか一つを使用します。

npm

npm i -S vuedraggable@next

yarn

yarn add vuedraggable@next

CDN

<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.10.2/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuedraggable@4.0.2/dist/vuedraggable.umd.min.js"></script>

 

直接「Vue.Draggable」のリポジトリを取得する場合は、以下のURLから取得できます。

https://github.com/SortableJS/vue.draggable.next

 

導入手順

管理人が行った、動作確認サンプルを実装するために、以下の手順でソースコードを導入していきます。
このサンプルは、1つのエリアの中で並び順を変更するものになります。

 

step.1 ライブラリの呼び出し

まずライブラリを呼び出す為に、以下の2通りのケースで呼び出します。

ES6等で実装する場合

import draggable from 'vuedraggable' 

CDNで実装する場合

const draggable = window['vuedraggable'];

 

step.2 テンプレートを準備

<draggable> の内部がドラッグ可能なエリアになります。

v-model プロパティにループする配列を設定します。list プロパティでも同様に行うことができますが、処理の仕方が少し違うようです。リストの囲みタグはtagプロパティへ設定します。uldivのような外部要素タグを設定して使用します。item-key にループ要素のkeyになる値を設定します。

<draggable> の内部は、自動でループ処理が行われます。Slotselementの中に、配列の個別データが格納されます。

ポイント

ループに使う配列は、data から直接渡しても良いですし、computed を使っても大丈夫です。

<div id="app">
  <draggable v-model="items" item-key="no" tag="ul">
    <template #item="{ element, index }">
      <li>{{element.name}}-(No.{{element.no}})</li>
    </template>
  </draggable>
</div>

注意ポイント

リストのitem-keyプロパティを忘れずに設定しましょう!
これが無いと要素の追加・削除で、バグが多発するので必ず設定しましょう!

 

step.3 Vueインスタンスを設定

Step1で取得したdraggablecomponents プロパティに設定します。

今回のサンプルはdateから直接ループに値を渡します。
リスト用の配列データitemsを設定します。

const App = {
  data() {
    return {
      items: [
        {no:1, name:'キャベツ', categoryNo:'1'},
        {no:2, name:'ステーキ', categoryNo:'2'},
        {no:3, name:'リンゴ', categoryNo:'3'}
      ]
    }
  },
  components: {
    draggable: draggable
  },
}
Vue.createApp(App).mount('#app');

 

step.4 リストにスタイルを適用する

Vue.DraggableにはjQuery uiのような専用のスタイルが無いので、見た目を整えるためにスタイルを自作する必要があります。

今回はサンプル用に下記のようなスタイルを適用します。

ul {
  list-style-type: none;
}
li {
  cursor: pointer;
  padding: 10px;
  border: solid #ddd 1px;
}

 

サンプル

今回のソースを実際に触って確認できるようにデモを用意しました。

 

複数エリアの導入手順

次に上の1つのエリア内で並び順を変更するサンプルを参考にして、二つのエリアをドラッグ移動できるサンプルを実装するための導入を行ってみます。
以下の手順にしたがって、実装を行っていきます。

 

1.テンプレートを準備

ドラッグ移動が可能なエリアを用意する為に、<draggable>を2つ準備します。

2つの<draggable>エリアにgroup プロパティでITEMSを持たせます。
これで2つ<draggable>エリアを自由に行き来させることができます。

<div id="app" class="container">
  <div id="box1" class="box">
    <draggable v-model="items" item-key="no" tag="ul" group="ITEMS">
      <template #item="{ element, index }">
        <li>{{element.name}}-(No.{{element.no}})</li>
      </template>
    </draggable>
  </div>
  <div id="box2" class="box">
    <draggable v-model="items2" item-key="no" tag="ul" group="ITEMS">
      <template #item="{ element, index }">
        <li>{{element.name}}-(No.{{element.no}})</li>
      </template>
    </draggable>
  </div>
</div>

注意ポイント

エリア内のリストが空になると再ドラッグがし辛くなります。
<draggable> に、スタイルでpadding:3pxみたいに厚みを持たせてあげるとドラッグがし易くなります

 

2. 複数のdraggableリスト用に配列を用意する

2つのドラッグエリア用のリストをdataに用意します。

const App = {
  data() {
    return {
      items:[
        {no:1, name:'キャベツ', categoryNo:'1'},
        {no:2, name:'ステーキ', categoryNo:'2'}
      ],
      items2:[
        {no:5, name:'きゅうり', categoryNo:'1'},
        {no:6, name:'ハンバーグ', categoryNo:'2'}
      ]
    }
  },
  components: {
    draggable: draggable
  },
}
Vue.createApp(App).mount('#app');
 

 

サンプル

今回のソースを実際に触って確認できるようにデモを用意しました。

 

イベント

ドラッグイベントは、任意のタイミングで動作します。
9種類のタイミングが用意されています。

 

イベント一覧

イベント タイミング
start ドラッグ開始したとき
add 要素が追加されたとき
remove 要素を削除されたとき
update ドラッグで要素の移動が完了したとき
end ドラッグが完了したときの最後に動作します
choose ドラッグ要素を選択したとき
unchoose ドラッグ要素を選択から外したとき
sort 並び順を変更したときのupdateの後、endの前に動作する
filter フィルターされたとき
clone ドラッグ時のシャドークローンが生成されたとき

 

イベント導入例

今回はendイベントを例に使って解説します。

step.1 onEnd関数を作成する

まずイベントを作成します。
onEndというイベントをmethodsに作成します。
onEndの第一引数を設定しておくと、ドラッグアイテムのイベント情報が格納されます。
これで、ドラッグ後にイベントを反応させられます。

methods: {
  onEnd: function(originalEvent){
    console.log(originalEvent);//originalEventは イベントを取得できる
  }
}

 

step.2 @end="onEnd"をコンポーネントに設定する

上記で作成したonEndイベントを<draggable>に設定します。

<draggable v-model="myArray" @end="onEnd">

 

リストに新規で追加する方法

あらかじめ用意されたリストに新規でドラッグ要素を追加したい場合は、以下が参考になります。

 

スクリプト

リストの配列にarray.pushを使って、新規のアイテムを追加します。
今回はdoAddというmedhodsを作成して、items配列にpush()でオブジェクトを追加していきます。

 
const App = {
  data() {
    return {
      items: [
        {no:1, name:'キャベツ', categoryNo:'1'},
        {no:2, name:'ステーキ', categoryNo:'2'},
        {no:3, name:'リンゴ', categoryNo:'3'}
      ],
      newNo: 4
    }
  },
  components: {
    draggable: draggable
  },
  methods: {
    doAdd:function(){
      let self = this;
      let no = 0;
      // itemsの中の一番大きなnoを取得して1を足す
      if(self.items.concat().length > 0){
         no = Math.max.apply(null,self.items.concat().map(function(item){return item.no;})) +1;
         self.newNo = self.newNo < no ? no:self.newNo;
      }
      // itemsにアイテムを追加
      this.items.push({
        no: this.newNo,
        name:'追加リスト'+ this.newNo,
        categoryNo:'5'
      });
    },
  }
}
Vue.createApp(App).mount('#app');

 

HTML

ボタンを用意して、クリックするとdoAddイベントが起動します。

<div id="app" class="container">
  <button v-on:click="doAdd">追加</button>
  <div class="p-3">
    <draggable v-model="items" item-key="no" tag="ul">
      <template #item="{ element, index }">
        <li>{{element.name}}-(No.{{element.no}})</li>
      </template>
    </draggable>
  </div>
</div>

 

サンプル

 

リストから削除する方法

リストの中からドラッグ要素を削除したい場合は、以下が参考になります。

 

スクリプト

リストの配列からarray.spliceを使って、アイテムを削除します。
doDeleteというmedhodsを作って、引数で配列のindexを受け取って、splice(index, 1)で削除します。

 
const App = {
  data() {
    return {
      items: [
        {no:1, name:'キャベツ', categoryNo:'1'},
        {no:2, name:'ステーキ', categoryNo:'2'},
        {no:3, name:'リンゴ', categoryNo:'3'}
     ],
     newNo: 4
    }
  },
  components: {
    draggable: draggable
  },
  methods: {
    doDelete: function(index){
      this.items.splice(index, 1);
    },
  }
}
Vue.createApp(App).mount('#app');

 

HTML

削除ボタンをクリックしてdoDeleteイベントを起動します。
引数にindexを持たせます。

<div id="app" class="container">
  <div class="p-3">
    <draggable v-model="items" item-key="no" tag="ul">
      <template #item="{ element, index }">
        <li>{{element.name}}-(No.{{element.no}}) <span class="del" v-on:click="doDelete(index)">[削除]</span></li>
      </template>
    </draggable>
  </div>
</div>

 

サンプル

 

Slots

プロパティ

Slotのプロパティはデフォルトですと、下記の2つになります。

  • element リストの要素
  • index リストのインデックス
<template #item="{ element:item, index:i }">

様に書くことで、別の変数名として取得することが可能です。

 

footer slot

<draggable v-model="myArray" item-key="id">
 <template #item="{element}">
   <div> {{element.name}} </div>
 </template>
 <template #footer>
   <button @click="addPeople">Add</button>
 </template>
</draggable>

 

header slot

<draggable v-model="myArray" item-key="id">
  <template #item="{element}">
    <div> {{element.name}} </div>
  </template>
  <template #header>
    <button @click="addPeople">Add</button>
  </template>
</draggable>

 

transition-group の設定方法

<draggable v-model="myArray" tag="transition-group" item-key="id">
  <template #item="{element}">
    <div> {{element.name}} </div>
  </template>
</draggable>

 

囲みタグを変更する場合は component-data プロパティに <transition-group> へ送るプロパティを設定します。

<draggable :component-data="{name: 'list',tag: 'ul'}" v-model="myArray" tag="transition-group" item-key="id">
  <template #item="{ element, index }">
    <li>{{element}}</li>
  </template>
</draggable>

 

ドラッグ時の掴める箇所やアニメーションを設定

Vue.Draggableは、Sortable.jsを元にしているので、同ライブラリのオプションを利用することができます。
例えば以下のようなオプションをしようすることができます。

カスタマイズできるモノ

  • ドラッグアイテムの掴める箇所を指定する(ハンドラーを設定)
  • ドラッグアイテムの移動時にアニメーションを設定する
  • ドラッグ可能にするまでに時間を設定する

 

Sortable.jsのオプションを実装してみたい方は、以下の記事が参考になります。

参考
Vue.DraggableでSortable.jsのオプションを使う

この記事は、「Vue.Draggable」でSortable.jsのオプションを使う方法を書いています。

続きを見る

 

リストの並び順をブラウザに保存する

WEBアプリなどでブラウザに並び順を保存したい時などは、下記の記事が参考になります。

参考
「Vue.Draggable」の並び順をブラウザに保存する方法

以前から「Vue.Draggable」の並び順をブラウザに保存したいなあと思っていました。先日「vue-ls」というブラザストレージを操作するライブラリの記事を書いた際に、勢いで「Vue.Draggable」と組み合わせたシステムを作成しました。

続きを見る

 

ドラッグリストをフィルタリングする

特定の文字を含むリストを並べ替えたり、カテゴリー絞り込みをしたい時の参考記事を書きました!↓

参考
Vue.Draggableでフィルタリングされたリストを使用する

Vue.Draggableを使いながらリストをフィルタする機能が欲しいと思いました。
しかし、Vue.Draggableの特性と言いますか、制限のようなものでcomputedが使用できません。
そこで別のやり方で実装しました。

続きを見る

 

テーブルの列を並び替える

テーブルの列の順番を並び替える方法を記事にしました。
下記の記事を参考にしてください。

後日記事にします。

 

さいごに

Vue.js製のドラッグ&ドロップコンポートネントライブラリでした。

管理人が実務で触った感触的には、ドラッグ&ドロップするならこのライブラリを選んでおけば問題ないと思います。
よっぽど特殊なことをやりたいなら、このライブラリをカスタマイズするよりも別のライブラリを使うのが無難だと思います。

そういう意味では、Vue.js製のドラッグ&ドロップ系ライブラリは結構種類が豊富です。

今後そういったライブラリをご紹介していければと思います。

 

おまけ

別のライブラリになってしまいますが、ツリー形式にドラッグ要素を配置できるものもあります。
これを使えばWordpressのメニュー的なのも作れそうですね。

参考
「vue-draggable-nested-tree」でドラッグ要素をツリー形式で配置する

vue-draggable-nested-treeは、ドラッグ要素をツリー形式に配置することが可能なdraggable&droppableコンポーネントです。tree-helper.js を使用することで、折りたたみを実装することも可能です。簡単に実装できるコピペと触って体感できるサンプルを掲載しています。

続きを見る

 

今日はこの辺でー

 

  • B!