【RailsAPI, nuxt.js, vuedraggable】vuedraggableで変更した情報をRailsで更新する方法

#前提
バックエンド：RailsAPI devise_token_authでユーザー認証実装済み, seriallizer実装済み
フロントエンド：nuxt.js モジュールモード、SPA

nuxt.jsではvuedraggableを活用してドラッグ＆ドロップ（以下D＆D）を実現します。
本記事ではvuedraggableの活用を、D＆Dの際にRails側でのDB更新の実装を記載します。

なお、nuxt.jsのアプリ作成とRailsのアプリ作成は対象にしません。

#目次
1. vuedraggableを使ってD＆Dを実現
2. Rails側で更新を走らせる

※コードだけ知りたい人はRailsとnuxtそれぞれのサンプルコードを公開していますので、文中のGitHubからダウンロードください。

#vuedraggalbeを使ってD＆Dを実現
下記コードをコンソールに打ち込みvuedraggableをインストール

```
$yarn add vuedraggable
```
vuedraggableインストール後は下記コードを実装してください。
このコードはコピーすることでnuxt.jsでのD＆Dを実装できます。
Rails側の実装をしていないので更新するとタスクの順番が元に戻ります。

![スクリーンショット 2021-05-23 17.26.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744524/520202e8-57cd-4ae7-c793-95c823483519.png)


```index.vue
<template>
  <div>
    <p>draggable検証</p>
    <div class="category">
      <p>title1</p>
      <draggable :list="arrayTitle1"
        ><div class="item" v-for="item in arrayTitle1" :key="item.no">
          {{ item.name }}
        </div></draggable
      >
    </div>
    <div class="category">
      <p>title2</p>
      <draggable :list="arrayTitle2"
        ><div class="item" v-for="item in arrayTitle2" :key="item.no">
          {{ item.name }}
        </div></draggable
      >
    </div>
  </div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
  components: {
    draggable,
  },
  data() {
    return {
      arrayTitle1: [
        { no: 1, name: 'キャベツ', categoryNo: '1' },
        { no: 2, name: 'ステーキ', categoryNo: '2' },
        { no: 3, name: 'リンゴ', categoryNo: '3' },
      ],
      arrayTitle2: [],
    }
  },
}
</script>
<style lang="scss">
.item {
  border: 1px solid white;
  margin: 3px;
  background-color: gray;
}
.category {
  border: 1px solid white;
  margin: 10px 5px;
  min-height: 150px;
}
</style>

```

####補足説明：
nuxt.jsでカテゴリ（タスクの移動先）を2つ作りました。
この2つの配列の中身をそれぞれ順番に表示するすることでタスクが表示されるようになっています。

<draggable> :listでデータにある配列と紐づけています。
Railsで更新する場合はこの紐付けを利用します。

####サンプルコード
https://github.com/saimaptw88/vue-draggable.git

####参考サイト：
https://javascript.plainenglish.io/building-a-kanban-board-with-vue-js-and-vuedraggable-in-15-minutes-496dbe3685e2


#更新を走らせる
まず実装イメージを掴んでもらいます。
①変更後のカテゴリを1つの配列にしてRailsにリクエストを送信します。
②Rails側でリクエストされた情報を全て更新してnuxt.js側に返します。

※nuxt.jsでタスクの順序は:listで紐付けた配列に保持されます。

####①変更後のカテゴリを1つの配列にしてRailsにリクエストを送信
```index.vue
 methods: {
    async updates() {
      const title1 = this.arrayTitle1
      const title2 = this.arrayTitle2

      // 移動後のカテゴリと並び順を保持
      title1.map((item, index) => {
        item.categoryNo = 1
        item.index = index
      })

      title2.map((item, index) => {
        item.categoryNo = 2
        item.index = index
      })

      // 二つの配列を一つにしてリクエストを送信する
      const allItems = title1.concat(title2)

      await this.$store.dispatch('items/updates', allItems)
    },
  },
```
※リクエストを受け取った後は各カテゴリの配列への分離をしてください。（この記事では省きます）


####②Rails側でリクエストされた情報を全て更新してnuxt.js側に返信
新規ルートとメソッドを宣言して、コントローラを作成します。

```routes.rb
Rails.application.routes.draw do
  get 'updates/update'
  resources :item
  patch "updates", to: "updates#update"
end

```


受け取ったパラメータをカテゴリごとに分離して、それぞれ繰り返し文で全て更新します。
更新後に再度1つの配列として結合してnuxt.jsへ返信します。

```wants_update_controller.rb
class UpdatesController < ApplicationController
  def update
    items = Item.all

    params["_json"].each do |i|
      item = items.find(i[:id])
      item.update(name: i[:name], categoryNo: i[:categoryNo], index: i[:index])
    end

    items = Item.order(index: :asc)
    items_title1 = []
    items_title2 = []

    items.each do |i|
      if i.categoryNo == 1
        items_title1.push(i)
      else
        items_title2.push(i)
      end
    end

    items_title1 = items_title1.each_with_index {|item, index| item.index = index }
    items_title2 = items_title2.each_with_index {|item, index| item.index = index }

    @items = items_title1.push(items_title2).flatten!
    render json: @items, each_serializer: ItemSerializer
  end
end
```

####サンプルコード
https://github.com/saimaptw88/vue-draggable-rails.git

以上です。
下記サイトも参考にしてみてください。
https://amaimon-decision.herokuapp.com/