Vue.js2系でorderByを使ってドラッグアンドドロップでDOMの並び順を変える
2017-10-01 2017-12-26
とあるプロジェクトで、「なんか流行ってるしVue.js使ってみよう」ということで、色々いじっていた時の副産物です。
Vue.jsは2系とそれ以前で仕様がかなり異なるため、1系で使えていたorderByなどのbuilt-inフィルタが使えなくなっており、computed(算出プロパティ)で使うしかなくなっています。
多分上述した理由で苦戦している人もいるかと思うので、メモ代わりに置いておきます。
学習コストが低いことがVue.jsの売りの一つだったと思いますが、そうでもないんじゃないかと思い始めています。
orderByを使うための前準備説明
公式サイトで「orderBy」と入れて検索するとここにたどり着くと思います。
要約すると「2系はcomputedで使えるよ!」とのことです。
1 2 3 4 5 |
computed: { orderedUsers: function () { return _.orderBy(this.users, 'name') } } |
ですが、いきなり「_.orderBy」と言われてもこちらも困ります。それvue.jsじゃなくね?
そうです。このアンダースコアから始まる関数は、Lodashというライブラリの関数です。こいつを読み込んであげないと、下記のjsは動きません。
ということでまずはhtmlから。
html
1 2 3 4 5 6 7 8 9 10 11 12 |
<body> <article id="orderField"> <section> <ul> <li v-for="item in orderedItems" v-html="item.dom" draggable="true" @dragstart="dragstart(item, $event)" @dragend="dragend" @dragenter="dragenter(item)"></li> </ul> </section> </article> <script src="https://npmcdn.com/vue/dist/vue.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script> <script src="/js/order.js"></script> </body> |
Vue.js的に注目すべきポイントはorderedItemsです。dataに設定しているitemsではなく、orderedItemsをv-forで利用していることに注目してください。
draggableとかはhtml5の領域なので、ここでの説明は省きます。
js
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 |
const object = new Vue({ el: '#orderField', data: { dragitem: null, items: [ {'dom' : '<p>こんにちわ</p>', 'order' : 1}, {'dom' : '<p>おはようございます</p>', 'order' : 2}, {'dom' : '<p>いただきます</p>', 'order' : 3}, ] }, methods: { dragstart: function (item, event) { // ドラッグしたitemを、this.dragitemにセットしておく this.dragitem = item; // ドラッグ中半透明に event.target.style.opacity = 0.3; }, dragenter: function (item) { // ドラッグ中に移動した箇所のorderを取得 const target_order = item.order; // ドラッグ中itemのorderを対象itemにセット item.order = this.dragitem.order; // 対象アイテムのorderをドラッグ中のitemにセット this.dragitem.order = target_order; }, dragend: function (event) { // ドロップしたitemを半透明から戻す event.target.style.opacity = 1; }, }, computed: { orderedItems: function () { return _.orderBy(this.items, 'order') } } }) |
vue.js的なポイントとしてはcomputedですが、ここは公式サイトに書いてある通りなので大丈夫でしょう。loadshのorderByを利用して、DOM要素の並び替えを行っています。
どちらかというと、dragenterの挙動で苦しむ人が多いんじゃないかと思います。dragenterはドラッグ中の要素が、ドロップ領域に入ったタイミングで発火するため、各DOM要素の上を通過したタイミングでorderが入れ替わることになります。
この例でいうと、「こんにちわ」をドラッグしたまま下にマウスカーソルをずらしていくと、「おはようございます」の要素に重なった瞬間に「こんにちわ」と「おはようございます」の位置が入れ替わり、「いただきます」を通過した後にドロップすることで、「おはようございます」「いただきます」「こんにちわ」の順に並ぶことになります。
こんにちわ
おはようございます
いただきます
↓
おはようございます
いただきます
こんにちわ
という感じです。
なお、このサンプルソースはドラッグ中にドロップ領域に重なったタイミングでorderが変わる仕組みとなっているため、ドラッグ中に他のDOM要素を避けるようにしてマウスカーソルを動かした場合、ドロップ領域として指定したDOM要素と位置が入れ替わることになります。
上記の例でいうと、「こんにちわ」をドラッグしたまま、「おはようございます」の要素を回避して、そのまま「いただきます」を通過した後にドロップした場合、「いただきます」「おはようございます」「こんにちわ」の順に並ぶということです。
これを避けたいのであれば、orderを振りなおすような仕組みが必要になります。