vuetifyのv-data-tableで入力テーブルを作る

# 概要
vuetifyのv-data-tableで直接テーブルデータを編集したい場合の書き方を自分なりに解説していく。
基本的な構文などは既知とする。
リアクティブの探求とか行の挿入/削除とかはやらない。

# 環境
- nuxt : 2.15.7
- vue : 2.6.14
- vuetify : 2.5.6

`.vue`ファイルに一括でビューやデータを書いていく。

# 書く
### とりあえずv-data-tableを使う
1行1列のデータを表示してみるソースの全体像はこんな感じ。
`v-card`使ってるのと、フッタを隠しているのは個人的な趣味。

```vue
<template>
  <v-app>
    <v-container>
      <v-row justify="start">
        <v-col cols="12">
          <v-card>
            <!-- とりあえず表示する -->
            <v-data-table
              :headers="headers"
              :items="items"
              hide-default-footer
            />
          </v-card>
        </v-col>
      </v-row>
    </v-container>
  </v-app>
</template>

<script>
export default {
  data() {
    return {
      headers: [{ text: "行1", value: "col1", align: "center" }],
      items: [{ col1: "hoge" }],
    }
  },
}
</script>
```

表示はこんな感じ。`data()`に書いた`items`がそのまま表示されているだけ。
![firstOfAll.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474496/ec37365b-ad07-4952-f27e-fa66845f62e7.png)

### テキストボックスにする
各カラムにはslotを使用して入力用コンポーネントを指定できる。→[参考](https://qiita.com/GeekMasahiro/items/79895d3b5740a62fdc8a)
[公式](https://vuetifyjs.com/ja/components/data-tables/#edit-dialog)にも載っている。

書いていない部分は上記したものから変わっていない。
(`solo`, `flat`, `hide-details` は趣味)

```vue
<v-data-table :headers="headers" :items="items" hide-default-footer>
  <!-- テキストボックス -->
  <template #[`item.col1`]="{ item }">
    <v-text-field 
      v-model="item.col1"
      solo
      flat
      hide-details 
    />
  </template>
</v-data-table>
```

これでデータが入力できるようになった。
![textField.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474496/44d857ce-15ee-b96b-4600-b6ec0705d822.png)

ただし注意として、ソートを有効にしてデータを入力すると、即座に順番が入れ替わってしまうので入力中はソートを切るなどの工夫が必要。`v-model.lazy`なども試したがうまくいかなかった。


### チェックボックスを追加する
テキスト入力だけでなく、チェックボックスとかラジオボタンとか普通のボタンとかも指定できる。
`style`はチェックボックス付近のみクリック可能に、`class`は位置をいい感じにしたいので。indigoは趣味。
`...`はソースコードを省略していることを表している(特別な記述とかではない)。

```vue
...
  <v-data-table :headers="headers" :items="items" hide-default-footer>
    <!-- テキストボックス(縦に長くなるので1行にまとめた) -->
    <template #[`item.col1`]="{ item }">
      <v-text-field v-model="item.col1" solo flat hide-details />
    </template>
    <!-- チェックボックス -->
    <template #[`item.col2`]="{ item }">
      <v-checkbox
        v-model="item.col2"
        hide-details
        color="indigo"
        style="width: 25px" 
        class="pa-0 ma-0 ml-5"
      />
    </template>
  </v-data-table>

...
<script>
...

  data() {
    return {
      headers: [
        { text: "行1", value: "col1", align: "center" },
        { text: "行2", value: "col2", align: "center" }, // チェックボックス用
      ],
      items: [{ col1: "hoge", col2: true }],
    }
  },

...
```

![checkbox.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474496/edd97aa6-9b25-60f0-f6e3-51c100a44f4e.png)

### ラジオボタンを追加する
行の中で複数の選択肢から1つを選ばせる場合はラジオボタンを使用することもできる。
セレクトボックスと見た目が違うだけなので、相当こだわりがない場合はセレクトボックスのほうが楽。
複数レコードのなかから1つ選ぶラジオボタンの場合は別途処理を記述する必要があるかも？

`v-layout`は1行で表示するため(このディレクティブを外せば縦に表示される)。
`class`は位置調整と、ラジオボタンが近づきすぎるのを防ぐため。

```vue
...
  <!-- ラジオボタン -->
  <template #[`item.col3`]="{ item }">
    <v-radio-group v-model="item.col3"> <!-- 各行でラジオボタンをグループ化 -->
      <v-layout row justify-center> <!-- ラジオボタンを横1行で表示する -->
        <v-radio
          v-for="r in radios"
          :key="r.id"
          :label="r.label"
          :value="r.id"
          class="my-0 mr-4"
        />
      </v-layout>
    </v-radio-group>
  </template>

...
<script>
...

  data() {
    return {
      headers: [
        { text: "行1", value: "col1", align: "center" },
        { text: "行2", value: "col2", align: "center" },
        { text: "行3", value: "col3", align: "center" },
      ],
      items: [{ col1: "hoge", col2: true, col3: 1 }],
      // ラジオボタンアイテム
      radios: [
        { id: 1, label: "foo" },
        { id: 2, label: "bar" },
      ],
    }
  },
...
```

![radio.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474496/ceeafd90-9b4a-faf0-9a8f-af4239c417c7.png)

### セレクトボックスを追加する
ラジオボタンと使用感は似ているが、セレクトボックスも使用可能。
ラジオボタンよりコードが簡潔になる。

```vue
...
  <!-- セレクトボックス -->
  <template #[`item.col4`]="{ item }">
    <v-select v-model="item.col4" :items="selects" solo flat hide-details />
  </template>

...
<script>
...

  data() {
    return {
     headers: [
       { text: "行1", value: "col1", align: "center" },
       { text: "行2", value: "col2", align: "center" },
       { text: "行3", value: "col3", align: "center" },
       { text: "行4", value: "col4", align: "center" },
     ],
...
    // セレクトボックスアイテム
    selects: [
      { text: "ace", value: 1 },
      { text: "bob", value: 2 },
    ],
  },
...

```


![selects.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/474496/b31688b0-5a8e-06a1-4e40-202842a43637.png)


# グリッドテーブル使ったらいいんじゃないの？
めぼしいのは以下くらいだった。

- [cheetah grid](https://github.com/future-architect/cheetah-grid)
- [vue-tabulator](https://vue-tabulator.netlify.app/)

ただし、書き方にクセがあったり制約があったりするので、意外とvuetifyが楽だったりする。

ちなみに、cheetah gridは`<canvas>`がうまく表示されずに挫折。
vue-tabulatorはチェックボックスの挙動がいまいちだったので挫折した。