とても簡単にドラッグアンドドロップが実現できる 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 を選びました
1 2 3 4 5 6 7 8 9 | $ 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をインストール
1 | $ yarn add vuedraggable |
TypeScript の定義ファイルを追加する
Vue-Draggable は TypeScript 対応されていないので、d.tsファイルを追加します
types/vuedraggable.d.ts
1 | declare module 'vuedraggable' |
tsconfig.jsonのcompilerOptionsのpathsに定義を追加します
1 2 3 4 5 6 7 8 9 10 | { "compilerOptions": { "paths": { "@/*": [ "src/*" ], "vuedraggable": [ "src/types/vuedraggable" ] }, |
※ 上記例は省略されています
ドラッグアンドドロップで要素を並び替えできるコンポーネントを作成します
ドラッグアンドドロップで要素を入れ替えることができるコンポーネントを Vue SFC(Single File Component) で実装していきます
スクリプト部分です
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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 の状態をアラート表示するメソッドを定義しています。
テンプレート部分です
1 2 3 4 5 6 7 8 9 10 | <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つのリストの要素を並び替え及び入れ替えるれるようにします
スクリプト部分です
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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の中身をフォーマットする関数を追加しています。
テンプレート部分です
1 2 3 4 5 6 7 8 9 10 11 | <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部分です。見た目を少しだけ整えてます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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;} |
ドラッグアンドドロップしている間をわかるようにする
要素を入れ替えるサンプルのコンポーネントをすこしだけ改修して、ドラッグアンドドロップしている間に色が変わる要素を追加してみたいと思います
スクリプト部分
1 2 3 4 5 6 7 8 | // ... 省略export default Vue.extend({ // ... 省略 data() { return { inDrag: false, //... 省略}) |
dataにドラッグアンドドロップ中の間に true となる inDrag メンバーを追加しています
テンプレート部分
1 2 3 4 5 6 7 8 9 10 11 | <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部分
1 2 3 4 5 6 7 8 9 10 11 | /* ...省略 */.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