11
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Vue.js】Vuetifyのv-data-tableでitemsをcomputedで取り扱いやすくすることについて

Last updated at Posted at 2019-12-31

概要

以下、Vue.jsのUIライブラリであるVuetifyの話です。
v-data-tableに描画したいデータをサーバから取得した際、受け取ったデータそのままだと扱いにくいのでなんとかしたいケースがあったので、その時のメモです。

基本的にサーバの返却値を修正してもらうのが諸事情で面倒くさいのでクライアント側でなんとかしたい、という前提での話です。

公式の1番簡単な使い方を基準に考えます。
※items-per-pageとclassは今回の話に不要なので除去しています。

公式のUsageのソース
<template>
  <v-data-table
    :headers="headers"
    :items="desserts"
  ></v-data-table>
</template>

Ajaxで受け取ったデータでdessertsを随時上書きするイメージで、そのデータが扱いにくいという状況です。
ケースに応じて調査してでてきたもの、力技でなんとかした対応を紹介します。

ケース1 チェックボックス(show-select)を使うとき、item-key用の一意のデータがない

コード1_show-selectを使う場合
<template>
  <v-data-table
    v-model="selected"
    :headers="headers"
    :items="desserts"
    item-key="name"
    show-select

  ></v-data-table>
</template>
<script>
export default {
  data () {
    return {
      selected: [],
      headers: [
        { text: '名前', value: 'name',  sortable: false },
        { text: '数値', value: 'value', sortable: false }
      ],
      desserts: [
        {
          name: 'data1',  value: 159,
          name: 'data1',  value: 200,
          name: 'data3',  value: 333,
        }
      ]
    }
  }
}
</script>

※data内で初期化してる部分がサーバからデータを受信したものとして見てください。

チェックボックスをテーブルに表示したい場合、show-selectした上でitem-keyにitemsのいずれかのkeyをセットする必要があります。
このとき、keyには一意の値がくるものを指定する必要があります。
コード1ではitem-key="name"として一意のkeyとしてnameが指定されていますが、サーバから取得したdessertsはnameの値がdata1で被っています。
この状況は嬉しくありません。
item-keyで指定したkeyの値が同一の場合、同じものと見なされてしまうためこの状況で数値が159か200の行のチェックボックスをクリックすると、クリックしなかったほうの行のチェックボックスまでチェックがついてしまうためです。
ちなみにこれはsingle-selectがどちらの場合でも両方にチェックがつきます。

こうならないためには一意の値をもつkeyを指定したいわけですが、サーバから返却されたデータにDBのPK的な固有の識別番号がないケースでは対応できません。
というわけでこれの対応が以下です。

一意のデータを作るcomputed
<template>
  <v-data-table
    v-model="selected"
    :headers="headers"
    :items="indexedItems"
    item-key="id"
    show-select
  ></v-data-table>
</template>
<script>
//~途中省略~
  computed: {
    indexedItems () {
      return this.desserts.map((item, index) => ({
        id: index,
        ...item
      }))
    }
  }
}
</script>

mapを使ってindexをそのままidとして持たせ、そのidをitem-keyで指定する形ですね。
こちらは公式のFeature Requestからの知見です。スマートですね。

ケース2 検索OKのテーブルで計算された値を使用したい

コード2_計算された値を使用する
<template>
  <v-data-table
    :headers="headers"
    :items="items"
    :search="search"
  ></v-data-table>
</template>
<script>
export default {
  data () {
    return {
      search: '',
      headers: [
        { text: '名前', value: 'name',  sortable: false },
        { text: '容量', value: 'capacity', sortable: false }
      ],
      items: [
        {
          name: 'data1',  value: 10485760,
          name: 'data2',  value: 20971520,
          name: 'data3',  value: 31457280,
        }
      ]
    }
  }
}
</script>

サーバから返却された容量の値がByte単位で、MBにしたいとします。
1024で2回割った値を使いたいわけなので、ケース1のようにcomputedを使ってあげればいいのはお分かりかと思います。

計算済みの値
<template>
  <v-text-field
    v-model="search"
  ></v-text-field>
  <v-data-table
    :headers="headers"
    :items="mbItems"
    :search="search"
  ></v-data-table>
</template>
<script>
//~途中省略~
  data () {
    return {
      search: '',
      headers: [
        { text: '名前', value: 'name',  sortable: false },
        { text: '容量', value: 'mb', sortable: false }
      ],
    }
  },
  computed: {
    mbItems () {
      return this.items.map((item) => ({
        mb: item.capacity / 1024 / 1024,
        ...item
      }))
    }
  }
}
</script>

computedは算出プロパティなんだからここでやるのが当然だろ、とお思いかもしれません。
ただv-data-tableでは以下のようにすることができます。

v-slotを使用するパターン
<template>
  <v-text-field
    v-model="search"
  ></v-text-field>
  <v-data-table
    :headers="headers"
    :items="items"
    :search="search"
  >
    <template v-slot:item.capacity="{ item }">
      {{ item.capacity / 1024 / 1024}}
    </template> 
  </v-data-table>
</template>

v-data-tableがもつv-slotのうちitem.<name>を使って容量のカラムだけtemplate側で計算するわけですね。
これでも表示は同じになるのですが、データの検索ができるテーブルではv-slotでの計算はよろしくありません。

なぜかと言うとtemlate側のv-slotで計算された値は、v-data-tableがもつsearchオプションでの検索にひっかかってくれないからです。
コード2の例だとMB単位で10,20,30のデータが表示されるわけですが、検索用のv-text-fieldに30といれても3行目がひっかかりません。
searchの値(30)を使ってitemsに対して検索をかけるため、
ちなみに上記の例だと10,20ではそれぞれ1行目,2行目がひっかかります。これは計算前の元からの値に含まれているためです。

このため、v-slotで描画時に計算するようにして10,20などで検索してみてOK!と軽く判断すると実はちゃんと検索がきいていない、ということになりかねません。
なので、検索可のテーブルに表示するデータを加工する場合はcomputedでやりましょう。(これが言いたかった)
私が最初v-slotでやっていて実は検索がきかなくて頭を抱えたのは内緒

まとめ

  • show-selectを使うテーブルでは、mapを使ってidを一意のデータとすることができます
  • データ検索ができるテーブルでは、計算済みデータを表示するときはcomputedでやりましょう
11
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?