【Vue.js】要素をドラッグ&ドロップできるようにする方法について解説します。【Vue.draggable.next】

Vue.js 便利ツール

【Vue.js】要素をドラッグ&ドロップできるようにする方法について解説します。【Vue.draggable.next】

なやむくん
Vue.jsで要素をドラッグ&ドロップできるようにしたい…。
なやむさん
おすすめの方法はありますか…。

などの疑問や悩みを解決してまいります。

ドラッグ&ドロップを実装したいものの、「実装に時間がかりそう」「難しそう」などイメージを持っている方も多いのではないでしょうか。

今回はVue.draggable.nextというVueプラグインを使用して、ドラッグ&ドロップを簡単に実装する方法と使い方について解説します。

Vue.draggable.nextとは

Vue.draggable.nextとは、Vue.jsを用いたプロジェクトでドラッグ&ドロップを実装するためのプラグインです。

Vue.draggable.nextはSortable.jsを元に作られているため、Vue.draggable.nextの公式ページに記載がなくてもSortable.jsの公式ページに記載がある内容も使用できます。

このプラグインを使用することで簡単にドラッグ&ドロップを実装することができます。

みつた
ドラッグ&ドロップって実装が難しそうに見えますが、ライブラリを使用することで簡単に実装することができますよ!

実装方法について

それではVue.draggable.nextの実装方法と、数ある機能のうちいくつかをご紹介していきます。

インストール

まずはプロジェクトにVue.draggable.nextをインストールします。

VSCodeでターミナルを開き、以下のコマンドを入力します。

npm i -S vuedraggable@next

入力しましたら、以下のようにインストールが実行されます。

インストールが完了しましたら、App.vueに以下のように記述します。

<script setup lang="ts">
import { ref } from 'vue'
import draggable from 'vuedraggable'

const peopleData = ref([
  { name: 'tanaka', id: 0 },
  { name: 'suzuki', id: 1 },
  { name: 'sato', id: 2 },
  { name: 'mori', id: 3 },
  { name: 'takago', id: 4 }
])
</script>

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

※上記コードはVuetifyを使用しています。コピペする場合は別途Vuetifyをインストールして下さい!

Vuetifyのインストール方法は以下にありますのでぜひ参考にしてください。

参考記事
【Vue.js】Vuetifyの導入方法について解説します。
【Vuetify3】Vuetifyの導入方法について解説します。

続きを見る

上記コードで以下のように実装ができると思います。

みつた
ドラッグ&ドロップをして順番が入れ替わればOKです!

<template #item="{ element }">の名称変更

elementの部分は以下のように:で名称を変更することができます。

<template #item="{ element: element2 }">
なやむくん
element固定だとさすがに使いづらいですよね…。

コンポーネント間の移動

ドラッグ&ドロップ1番の目玉機能はやはりコンポーネント間の移動です。
移動させたいコンポーネントのdraggableタグにgroupを指定します。

<script setup lang="ts">
import { ref } from 'vue';
import draggable from 'vuedraggable';

const peopleData = ref([
  { name: 'tanaka', id: 0 },
  { name: 'suzuki', id: 1 },
  { name: 'sato', id: 2 },
  { name: 'mori', id: 3 },
  { name: 'takago', id: 4 }
]);
const peopleData2 = ref([]);
</script>

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>
      
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

以下のようにコンポーネント間を移動できるようになればOKです。

ドラッグ、ドロップ時にイベント処理

ドラッグ、ドロップ時それぞれにイベント処理を発生させたい場合はドラッグ時は@start、ドロップ時は@endを使用します。

<script setup lang="ts">
import { ref } from 'vue'
import draggable from 'vuedraggable'

const peopleData = ref([
  { name: 'tanaka', id: 0 },
  { name: 'suzuki', id: 1 },
  { name: 'sato', id: 2 },
  { name: 'mori', id: 3 },
  { name: 'takago', id: 4 }
])
const peopleData2 = ref([])

// コンソールで実行確認
const testStart = () => {
  console.log('ドラッグ時に実行')
}
const testEnd = () => {
  console.log('ドロップ時に実行')
}
</script>

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable
            v-model="peopleData"
            group="people"
            item-key="id"
            @start="testStart"
            @end="testEnd"
          >
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>

      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

以下のようにコンソールに表示されればOKです。

順番の入れ替え

コンポーネント内で順番の入れ替えの可否sortプロパティで設定ができます。

boolean値で可否が設定でき、デフォルトはtrueなので入れ替えができるようになっていますが、falseにすると入れ替えができなくなります。

<script setup lang="ts">
import { ref } from 'vue'
import draggable from 'vuedraggable'

const peopleData = ref([
  { name: 'tanaka', id: 0 },
  { name: 'suzuki', id: 1 },
  { name: 'sato', id: 2 },
  { name: 'mori', id: 3 },
  { name: 'takago', id: 4 }
])
const peopleData2 = ref([])
</script>

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id" :sort="false">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>

      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id" :sort="false">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

以下のように順番の入れ替えができなくなればOKです。

ドラッグ&ドロップの可否設定

要素をドラッグ&ドロップできなくする場合は、:disabledを使用します。

<script setup lang="ts">
import { ref } from 'vue'
import draggable from 'vuedraggable'

const peopleData = ref([
  { name: 'tanaka', id: 0 },
  { name: 'suzuki', id: 1 },
  { name: 'sato', id: 2 },
  { name: 'mori', id: 3 },
  { name: 'takago', id: 4 }
])
const peopleData2 = ref([])
</script>

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id" :sort="false" :disabled="true">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>

      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id" :sort="false">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

上記の例ではpeopleData:disabledを設定しています。

boolean値でドラッグ&ドロップの可否を設定でき、デフォルトはtrueです。

みつた
ドラッグ&ドロップができなくなっているので、テキストを選択してしまう状態になっていますね・・・。

移動したタイミングでイベント発生

@endドロップしたタイミングでイベントを発生させますが、Moveを使用することでドラッグした要素がリスト内、または異なるリスト間を移動したタイミングでイベントを発生させることできます。

// 処理省略
const testConsole = () => {
  console.log('移動時にイベント発生')
}
</script>

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id" :sort="false" :move="testConsole">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>

      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id" :sort="false">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

リスト内、または別のリストにドラッグするとイベント処理が実行されているのが分かるかと思います。

選択した要素にクラスを付与

選択したドラッグ要素に対してクラスを付与することができるのが、chosenClassです。

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id" chosenClass="bg-red">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>

      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id" chosenClass="bg-blue">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

<style scoped>
.bg-red {
  background-color: #e53935;
}
.bg-blue {
  background-color: #35a4e5;
}
</style>

選択時にクラスが付与され、ドラッグしている間も背景色が付与されます。

移動時にクラス付与

dragClassを指定することで、ドラッグして移動している間に限りクラスを付与することもできます。

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id" :sort="false" dragClass="bg-red">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>

      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id" :sort="false">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

<style scoped>
.bg-red {
  background-color: #e53935;
}
.bg-blue {
  background-color: #35a4e5;
}
</style>

chosenClassのときは選択時と移動時も背景色が変更されていることを確認できたと思いますが、dragClassだと選択時は背景色が変わらず、移動時のみクラスが付与されていることが分かります。

dragClass指定時のドラッグ&ドロップしているときの要素【背景赤】
dragClass指定時のドラッグ&ドロップしているときの要素【背景青】

指定したリスト内に移動したタイミングでクラス付与

ghostClassをリストに指定することで、その指定したリスト内に移動したタイミングでドラッグ要素に対してクラスを付与することができます。

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id" :sort="false" ghostClass="bg-red">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>

      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id" :sort="false">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

<style scoped>
.bg-red {
  background-color: #e53935;
}
.bg-blue {
  background-color: #35a4e5;
}
</style>

以下のように、左のリスト内でドラッグを開始したときは背景が赤になり、右側のリスト内に移動したときは青色に変化していることが分かります。

ドラッグ&ドロップの要素の移動をスムーズにする

animationを指定することで、リスト内の要素の移動をスムーズにすることができます。

<template>
  <v-container>
    <v-row>
      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData" group="people" item-key="id" :sort="false" :animation="300">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataデータの中身:{{ peopleData }}</div>
      </v-col>

      <v-col>
        <v-list :border="true" class="pa-8">
          <draggable v-model="peopleData2" group="people" item-key="id" :sort="false">
            <template #item="{ element: element2 }">
              <v-list-item :border="true" class="pa-8 mt-4">
                {{ element2.id }}. {{ element2.name }}
              </v-list-item>
            </template>
          </draggable>
        </v-list>
        <div>peopleDataの中身:{{ peopleData2 }}</div>
      </v-col>
    </v-row>
  </v-container>
</template>

以下のように要素の入れ替えの動きが滑らかになったのが分かるかと思います。

まとめ

ここまで読んでいただきありがとうございました。
本記事のまとめです。

ポイント

  • Vue.draggable.nextは、Vue.jsを用いたプロジェクトでドラッグ&ドロップを実装するためのプラグイン
  • elementの部分は:で区切り、名称を変更することができる
  • 移動させたいコンポーネントのdraggableタグにgroupを指定
  • ドラッグ、ドロップ時それぞれにイベント処理を発生させたい場合はドラッグ時は@start、ドロップ時は@endを指定
  • コンポーネント内で順番の入れ替えの可否sortプロパティで設定
  • 要素をドラッグ&ドロップできなくする場合は、:disabledを指定
  • Moveを使用することでドラッグした要素がリスト内、または異なるリスト間を移動したタイミングでイベントを発生させることできる
  • chosenClassを指定することで、選択したドラッグ要素に対してクラスを付与することができる
  • dragClassを指定することで、ドラッグして移動している間に限りクラスを付与することができる
  • ghostClassを指定することで、その指定したリスト内に移動したタイミングでドラッグ要素に対してクラスを付与することができる
  • animationを指定することで、リスト内の要素の移動をスムーズにすることができる
  • この記事を書いた人
  • 最新記事

みつた

完全未経験&異業種から30歳の年でIT企業に転職。

Web系開発言語が好き。
どちらかというとバックエンドよりもフロントエンドが好き(現時点では…)

最近はサウナと観葉植物にハマっている。
野球が好きで一応投手。

-Vue.js, 便利ツール