🐈

VuetifyのDataTableをいじってみる

2022/06/14に公開

https://vuetifyjs.com/ja/components/data-tables/#slots

基本形

  • headersにテーブルヘッダー情報
  • itemsにテーブルデータ情報
Table.vue

<template>
  <div>
    <v-data-table
      :headers="headers"
      :items="items"
      :items-per-page="5"
      class="elevation-1"
    />
  </div>
</template>

<script>
import { defineComponent } from "@vue/composition-api"

export default defineComponent({
  setup() {
    const headers = [
      {
        text: "Dessert (100g serving)",
        align: "start",
        sortable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
    ]

    const items = [
      {
        name: "Frozen Yogurt",
        calories: 159,
        fat: 6.0,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
      },
      {
        name: "Eclair",
        calories: 262,
        fat: 16.0,
      },
    ]
    return { headers, items }
  },
})
</script>

再利用可能なテーブルにする(基本)

propsでheadersとitemsを受け取れるようにする

Table.vue
<template>
  <div>
    <v-data-table
      :headers="headers"
      :items="items"
      :items-per-page="5"
      class="elevation-1"
    />
  </div>
</template>

<script>
import { defineComponent } from "@vue/composition-api"

export default defineComponent({
  props: {
    headers: Array,
    items: Array,
  },
  setup() {
    return {}
  },
})
</script>
Home.vue
<template>
  <div>
    <Table :headers="headers" :items="items" />
  </div>
</template>

<script>
import { defineComponent } from "@vue/composition-api"
import Table from "@/components/Table"
import VslotTemplate from "@/components/VslotTemplate"

export default defineComponent({
  components: {
    Table,
    VslotTemplate,
  },
  setup() {
    const headers = [
      {
        text: "Dessert (100g serving)",
        align: "start",
        sortable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
    ]

    const items = [
      {
        name: "Frozen Yogurt",
        calories: 159,
        fat: 6.0,
      },
      {
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
      },
      {
        name: "Eclair",
        calories: 262,
        fat: 16.0,
      },
    ]

    return { headers, items }
  },
})
</script>

再利用可能なテーブルにする(slot編)

例えばテーブルの、1番目のカラムにアイコンを表示したい場合。

v-slot:item.<columName>を使って、アイコンを挿入する(上書き)

Table.vue
<template>
  <div>
    <v-data-table
      :headers="headers"
      :items="items"
      :items-per-page="5"
      class="elevation-1"
    >
      <template v-slot:item.actions>
        <v-btn icon><v-icon>mdi-pencil</v-icon></v-btn>
        <v-btn icon><v-icon>mdi-menu</v-icon></v-btn>
      </template>
    </v-data-table>
  </div>
</template>

<script>
import { defineComponent } from "@vue/composition-api"

export default defineComponent({
  props: {
    headers: Array,
    items: Array,
  },
  setup() {
    return {}
  },
})
</script>

headersに空カラムを追加 + itemsの各itemに空文字を設定する

Home.vue
<template>
  <div>
    <Table :headers="headers" :items="items" />
  </div>
</template>

<script>
import { defineComponent } from "@vue/composition-api"
import Table from "@/components/Table"
import VslotTemplate from "@/components/VslotTemplate"

export default defineComponent({
  components: {
    Table,
    VslotTemplate,
  },
  setup() {
    const headers = [
      { text: "", value: "actions" }, //追加
      {
        text: "Dessert (100g serving)",
        align: "start",
        sortable: false,
        value: "name",
      },
      { text: "Calories", value: "calories" },
      { text: "Fat (g)", value: "fat" },
    ]

    const items = [
      {
        actions: "", //追加
        name: "Frozen Yogurt",
        calories: 159,
        fat: 6.0,
      },
      {
        actions: "", //追加
        name: "Ice cream sandwich",
        calories: 237,
        fat: 9.0,
      },
      {
        actions: "", //追加
        name: "Eclair",
        calories: 262,
        fat: 16.0,
      },
    ]

    return { headers, items }
  },
})
</script>

ただし、、、
これだと、固定アイコンが入ってしまうので、再利用性が低い。
なので、このように変更する。

Table.vue
<template>
  <div>
    <v-data-table
      :headers="headers"
      :items="items"
      :items-per-page="5"
      class="elevation-1"
    >
      <template v-slot:item.actions>
        <slot name="actions"> </slot>
      </template>
    </v-data-table>
  </div>
</template>

<script>
import { defineComponent } from "@vue/composition-api"

export default defineComponent({
  props: {
    headers: Array,
    items: Array,
  },
  setup() {
    return {}
  },
})
</script>

Table.vueファイルで作った独自スロット(name="actions")にアイコンを書く

Home.vue
<template>
  <div>
    <Table :headers="headers" :items="items">
      <template #actions>
        <v-btn icon><v-icon>mdi-pencil</v-icon></v-btn>
        <v-btn icon><v-icon>mdi-menu</v-icon></v-btn>
      </template>
    </Table>
  </div>
</template>
<script>
//scriptは上と同じなので省略、
</script>

さらに再利用度を高めていく

編集ボタンを押したときに、itemのIDを取得する動きを実現したい。
(編集ページにitemのIDを渡すことが多いため)

vue slotの基礎
https://jp.vuejs.org/v2/guide/components-slots.html

まず、itemsを参照できるようにするため、v-slotテンプレートに="items"を追記する

<template v-slot:item.actions>
↓
<template v-slot:item.actions="items">

さらに、独自スロットにIDを渡せるようにする。
これをすることで、Home.vueでidを参照することが出来るようになる。

<slot name="actions" :id="items.item.id" />
Table.vue
<template>
  <div>
    <v-data-table
      :headers="headers"
      :items="items"
      :items-per-page="5"
      class="elevation-1"
    >
      <template v-slot:item.actions="items">
        <slot name="actions" :id="items.item.id" />
      </template>
    </v-data-table>
  </div>
</template>

ちなみに、v-slot:item.actions="items"のitemsの中身は
こういうデータ構造になっている。

{
  "item": {
    "actions": "",
    "id": "1",
    "name": "Frozen Yogurt",
    "calories": 159,
    "fat": 6,
    "carbs": 24,
    "protein": 4,
    "iron": "1%"
  },
  "isMobile": false,
  "header": {
    "text": "",
    "value": "actions"
  },
  "index": 0,
  "value": ""
}

なので、分割代入を使って、こう書くことも可能。

Table.vue
<template v-slot:item.actions="{item}">
  <slot name="actions" :id="item.id" />
</template>

Home.vueでは、このようにして、Table.vueのslotで設定したプロパティ(id="item.id")にアクセスできるようにする。

<template #actions="slotProps">

ちなみに、このslotPropsはお好みの名前に変更しても問題ない。

Home.vue
<template>
    <Table :headers="headers" :items="items">
      <template #actions="slotProps">
        <v-btn icon @click="openEditPage(slotProps.id)">
          <v-icon>mdi-pencil</v-icon>
        </v-btn>
        <v-btn icon><v-icon>mdi-menu</v-icon></v-btn>
      </template>
    </Table>
</template>

これも分割代入を使うことで、短く書ける。

Home.vue
<template>
    <Table :headers="headers" :items="items">
      <template #actions="{id}">
        <v-btn icon @click="openEditPage(id)">
          <v-icon>mdi-pencil</v-icon>
        </v-btn>
        <v-btn icon><v-icon>mdi-menu</v-icon></v-btn>
      </template>
    </Table>
</template>

Discussion