JQ Blog

VuejsにおけるDrag&Drop

Rails5.1.1でのVuejs

1. HTML5を利用したDrag&Drop

Rails5.1.1の環境で実装してみた。ビューとJS側はただ宣言と登録だけして全てのコードは.vueファイルに書いておいた。 - ビューとJS側

index.html.slim

1
2
3
4
#todo
  todo-dnd

= javascript_pack_tag 'todo'

todo.js

1
2
3
4
5
6
7
8
import Vue from 'vue/dist/vue.esm'
import TodoDnd from './todo-dnd.vue'

Vue.component('todo-dnd', TodoDnd)

new Vue({
  el: '#todo'
})
  • vueファイル

todo-dnd.vue

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<template>
  <div class="mainDiv">
    <div class="div">
      <ul>
        <li v-for="list in lists" draggable="true" @dragstart="dragStart(list, $event)">
          <input type="checkbox" v-model="list.todoDone">
          <label v-show="!list.todoDone"></label>
          <label v-show="list.todoDone"><s></s></label>
        </li>
      </ul>
    </div>
    <div class="div">
      <textarea cols="50" rows="10" @dragover="dragOver($event)" @dragleave="dragLeave($event)" @dragover.prevent @drop="drop($event)"></textarea>
    </div>
    <div>
      <button v-on:click="afterSend">送信</button>
    </div>
  </div>
</template>

<script>
export default {
  data: function(){
    return {
      lists: [
        {name: "Ruby", content: 'Hello, Ruby!', todoDone: false},
        {name: "Rails", content: 'Hello, Rails!', todoDone: false},
        {name: "Vue.js", content: 'Hello, Vue.js!', todoDone: false}
      ],
      listIndex: null,
      dragContent: ''
    }
  },
  mounted: function(){

  },
  props: [],
  methods: {
    dragStart: function(item, evt) {
      this.dragContent = item.content;
      this.listIndex = this.lists.indexOf(item);
    },
    dragOver: function(evt) {
      evt.target.setAttribute('placeholder', this.dragContent);
    },
    dragLeave: function(evt) {
      evt.target.removeAttribute('placeholder');
    },
    drop: function(evt) {
      evt.target.removeAttribute('placeholder');
      evt.target.value = this.dragContent;

    },
    afterSend: function() {
      this.lists[this.listIndex].todoDone = true;
    },
  },
}
</script>

<style scoped>
.mainDiv {
  width: 100%;
}
.div {
  float: left;
  width: 100%;
  margin-right: 100px;

  ul {
    width: 300px;
    border: 1px solid black;

    li {
      border: 1px solid black;
      margin-top: 5px;
    }
  }
}
</style>

Vuejsで提供してくれるEventの一部

@click
@submit
@keyup
@mousedown
@mousemove
@mouseup
@mouseleave
@touchstart
@touchmove
@touchend
@dragstart
@dragenter
@dragleave
@dragover
@dragend

この中でdrag&dropに関するEventもあるのでそれらを活用してVuejsとHTML5を連携する。 テンプレート側で、 Dragする対象にdraggable="true"にしてDragが可能になるようにする。あと@dragstartをかけてdragが始まったときのEventを処理する。 Dropされる対象に@dragover,@dragleave,@dropをかけてそれぞれの必要な処理をさせる。

2. VueDraggableを利用したDrag&Drop

ビューとJS側はさっきのやつと同じようになるのでvueファイルだけ書く。 VueDraggableを使うためには二つの方法がある。

1
2
3
4
5
6
7
8
import draggable from 'vuedraggable'
...
export default {
      components: {
          draggable,
          ...
      }
      ...

こういうふうにimportをするか、

1
var draggable = require('vuedraggable')

requireをする。 今回は前者のimportの方法を使う。importをするにはnpmbowerでインストールをする。今回はnpmで。

1
$ npm install vuedraggable
  • vueファイル
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<template>
  <div>
    <div>
      <h2>Simple DnD</h2>
      <ul>
        <draggable v-model="lists" :options="{group:'people'}" @start="drag=true" @end="drag=false">
          <li v-for="list in lists"></li>
        </draggable>
      </ul>
    </div>
    <hr>
    <div class="div2">
      <h2>Two Lists</h2>
      <div class="twoListsDiv">
        <ul>
          <draggable v-model="lists" :options="{group:'people'}">
            <li v-for="list in lists"></li>
          </draggable>
        </ul>
      </div>
      <div class="twoListsDiv">
        <ul>
          <draggable v-model="lists2" :options="{group:'people'}">
            <li v-for="list in lists2"></li>
          </draggable>
        </ul>
      </div>
    </div>
    <hr>
    <div>
      <h2>List clone</h2>
      <div class="listCloneDiv">
        <ul>
          <draggable v-model="lists" :options="{group:{ name:'people',  pull:'clone', put:false }}">
            <li v-for="(list, index) in lists" :key="index"></li>
          </draggable>
        </ul>
      </div>
      <div class="listCloneDiv">
        <ul>
          <draggable v-model="lists2" :options="{group:'people'}">
            <li v-for="(list, index) in lists2" :key="index"></li>
          </draggable>
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
  components: {
    draggable,
  },
  data: function() {
    return {
      lists: [{name: 'Ruby'}, {name: 'Rails'}, {name: 'Vuejs'}],
      lists2: [{name: 'Java'}, {name: 'Swift'}, {name: 'Objective-C'}],
    }
  }
}

</script>
<style>
div {
  ul {
    width: 200px;
    border: 1px solid
  }
}
.twoListsDiv, .listCloneDiv {
  float: left;
}
.div2 {
  overflow: hidden;
}
</style>

VueDraggableではdraggableというタグを使う。このタグを使っていろんなDnDができる。optionsを使って同じグループに結んだらDnDを簡単に実装することができるし、pullとかputとかでDnDのOptionを決めることができる。

参考

Vue.jsのリストレンダリングとhtml5のドラッグ&ドロップの実装
ネイティブ HTML5 ドラッグ&ドロップ
require is not definedを解消してrequireを使えるようにする