とても簡単にドラッグアンドドロップが実現できる Vue-draggable を使ってみた
西田@大阪です
以前 v-kansai Vue.js/Nuxt.js meetup #13 に参加させていただいた時に気になっていた Vue.js でドラックドロップでリストを入れ替えることができる Vue-draggable を使ってみました
SortableJS/Vue.Draggable: Vue drag-and-drop component based on Sortable.js
プロジェクトの作成
Vue.jsのプロジェクトを作成します。今回は今流行りの TypeScript を選びました
$ vue create vue-draggable-sample ? Please pick a preset: Manually select features ? Check the features needed for your project: TS ? Use class-style component syntax? No ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? Yes ? Save preset as:
Vue-draggableをインストール
$ yarn add vuedraggable
TypeScript の定義ファイルを追加する
Vue-Draggable は TypeScript 対応されていないので、d.ts
ファイルを追加します
types/vuedraggable.d.ts
declare module 'vuedraggable'
tsconfig.json
のcompilerOptions
のpaths
に定義を追加します
{ "compilerOptions": { "paths": { "@/*": [ "src/*" ], "vuedraggable": [ "src/types/vuedraggable" ] },
※ 上記例は省略されています
ドラッグアンドドロップで要素を並び替えできるコンポーネントを作成します
ドラッグアンドドロップで要素を入れ替えることができるコンポーネントを Vue SFC(Single File Component) で実装していきます
スクリプト部分です
import Vue from 'vue' import draggable from 'vuedraggable' export default Vue.extend({ name: "Draggable", components: { draggable }, data() { return { items: [ { id: 1, name: "ITEM - 1" }, { id: 2, name: "ITEM - 2" }, { id: 3, name: "ITEM - 3" }, { id: 4, name: "ITEM - 4" }, ] } }, methods: { showItems() { alert(this.items.map(val => { return val.id })) } } })
Vue-Draggable をコンポーネントとして使えるよう登録し、data
にitems
という名前でドラッグアンドドロップさせるデータを配列として登録しています。ドラッグアンドドロップした際にこの配列順番が入れ替わります。showItems
という名前で、現在の items
の状態をアラート表示するメソッドを定義しています。
テンプレート部分です
<template> <div> <draggable v-model="items" draggable=".item"> <div v-for="item in items" :key="item.id" class="item"> {{item.name}} </div> </draggable> <button @click="showItems">表示</button> </div> </template>
draggable
コンポーネントに v-model
でitems
を渡しています。また、draggable
属性にドラッグアンドドロップする要素を特定するためのセレクタを指定しています。上記例だとitemクラス
を指定しています
これだけで、実際にドラッグアンドドロップでき、入れ替わった値がv-model
で反映されていることがわかります
ドラッグアンドドロップで2つのリストの要素を入れかえできるコンポーネントを作成します
ドラッグアンドドロップして2つのリストの要素を並び替え及び入れ替えれるようにします
スクリプト部分です
import Vue from 'vue'; import draggable from 'vuedraggable'; function dumpObj(obj: any): string { return JSON.stringify(obj, null, 2) } export default Vue.extend({ name: "Swap", components: { draggable }, data() { return { items1: [ { id: 1, name: "ITEM - 1" }, { id: 2, name: "ITEM - 2" }, { id: 3, name: "ITEM - 3" }, { id: 4, name: "ITEM - 4" }, ], items2: [ { id: 5, name: "ITEM - 5" }, { id: 6, name: "ITEM - 6" }, { id: 7, name: "ITEM - 7" }, { id: 8, name: "ITEM - 8" }, ] } }, computed: { formattedItems1(): string { return dumpObj(this.items1); }, formattedItems2(): string { return dumpObj(this.items2); }, } })
items1
とitems2
という2つの配列を用意してます。この配列間でドラッグアンドドロップ可能になり、要素が入れ替わります。dumpObj
というJSON表示用の関数を追加し、computed
のメンバーとしてitemsの中身をフォーマットする関数を追加しています。
テンプレート部分です
<div class="container"> <draggable v-model="items1" draggable=".item" group="items"> <div v-for="item in items1" :key="item.id" class="item">{{item.name}}</div> </draggable> <draggable v-model="items2" draggable=".item" group="items"> <div v-for="item in items2" :key="item.id" class="item">{{item.name}}</div> </draggable> <div><pre>{{formattedItems1}}</ pre></div> <div><pre>{{formattedItems2}}</ pre></div> </div>
2つのVue-Draggable要素とそれぞれに設定されたdata
の値を表示する要素を並べています。Vue-Draggable要素ののgroup
属性にドラッグアンドドロップで入れ替え可能にしたいVue-Draggable要素で同じ値を設定します。上の例だと2つのVue-Draggable要素のgroup
属性にitems
と同じ文字列を指定しています
CSS部分です。見た目を少しだけ整えてます
pre { text-align: start; background: #2c3e50; color: white; padding: 10px; font-weight: bold; } .item { padding: 5px; } .container { margin: auto; width: 600px; display: flex; justify-content: space-around; }
ドラッグアンドドロップしている間をわかるようにする
要素を入れ替えるサンプルのコンポーネントをすこしだけ改修して、ドラッグアンドドロップしている間に色が変わる要素を追加してみたいと思います
スクリプト部分
// ... 省略 export default Vue.extend({ // ... 省略 data() { return { inDrag: false, //... 省略 })
data
にドラッグアンドドロップ中の間に true
となる inDrag
メンバーを追加しています
テンプレート部分
<div> <div class="indicator" :class="{ inMove: inDrag }"></div> <div class="container"> <draggable v-model="items1" draggable=".item" group="items" @start="inDrag=true" @end="inDrag=false"> <div v-for="item in items1" :key="item.id" class="item">{{item.name}}</div> </draggable> <draggable v-model="items2" draggable=".item" group="items" @start="inDrag=true" @end="inDrag=false"> <div v-for="item in items2" :key="item.id" class="item">{{item.name}}</div> </draggable> <!-- ... 省略 -->
ドラッグアンドドロップ中に色が変わる class属性にindicator
クラスが設定された要素が追加されています。また Vue-Draggable
の @start
属性にinDrag
をtrue
に設定するコード、@end
属性にinDrag
をfalse
に設定するコードが設定されています。それぞれ、ドラッグアンドドロップの開始と終了に対応しているイベントハンドラーになります
CSS部分
/* ...省略 */ .inMove { background: brown !important; } .indicator { margin: auto; width: 300px; height: 30px; background: #42b983; }
色が変わるよう見た目を調整しています
最後に
とても簡単にドラッグアンドドロップを実現できて驚きです。今後 Vue でドラッグアンドドロップを実装する機会があればぜひ使ってみようとおもいます。この記事が誰かの参考になれば幸いです
参考
SortableJS/Vue.Draggable: Vue drag-and-drop component based on Sortable.js