[Vuetify] V-Data-Table で複数項目のフィルタリングを行う方法

# 目的
本記事の目的は、Vuetifyの`<v-data-table>`を使用して、複数条件で絞り込み、動的にテーブルの表示を切り替えることである。
# やったこと
はじめに[VuetifyのV-Data-TableのAPI](https://vuetifyjs.com/ja/api/v-data-table/) を読んで、`custom-filter`プロパティを使用しようと考えた。
しかし、`custom-filter`プロパティの引数である`search`は一つしか変数を持つことができない。
![スクリーンショット 2021-02-06 17.42.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394701/8c9acdb4-69db-d605-88a4-6b9b661d4fdf.png)
そこで、V-Data-TableのAPIのheadersに記述されている`filter`プロパティを使用することで複数項目のフィルタリングを可能にした。
![スクリーンショット 2021-02-06 17.45.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394701/122543e4-7ac7-2679-e3aa-c9b2fc2f1b07.png)




## 作成画面
![スクリーンショット 2021-02-06 16.56.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394701/b6c09927-231b-92d6-f8f4-a5a5f84bcaa4.png)


## 使用環境
- Vue.js: "^2.6.11",
- Vue-Router: "^3.2.0",
- Vuetify: "^2.3.21"

## ファイルの説明

- sampleDataTable.json : テーブルに格納するサンプルデータ
- Table.vue : テーブルを表示させる為のVueファイル

## ソースコード

```sampleDataTable.json
{
  "data": [
    {
      "name": "Frozen Yogurt",
      "calories": 159,
      "fat": 6.0,
      "carbs": 24,
      "protein": 4.0,
      "iron": "1%"
    },
    {
      "name": "Ice cream sandwich",
      "calories": 237,
      "fat": 9.0,
      "carbs": 37,
      "protein": 4.3,
      "iron": "1%"
    },
    {
      "name": "Eclair",
      "calories": 262,
      "fat": 16.0,
      "carbs": 23,
      "protein": 6.0,
      "iron": "7%"
    },
    {
      "name": "Cupcake",
      "calories": 305,
      "fat": 3.7,
      "carbs": 67,
      "protein": 4.3,
      "iron": "8%"
    },
    {
      "name": "Gingerbread",
      "calories": 356,
      "fat": 16.0,
      "carbs": 49,
      "protein": 3.9,
      "iron": "16%"
    },
    {
      "name": "Jelly bean",
      "calories": 375,
      "fat": 0.0,
      "carbs": 94,
      "protein": 0.0,
      "iron": "0%"
    },
    {
      "name": "Lollipop",
      "calories": 392,
      "fat": 0.2,
      "carbs": 98,
      "protein": 0,
      "iron": "2%"
    },
    {
      "name": "Honeycomb",
      "calories": 408,
      "fat": 3.2,
      "carbs": 87,
      "protein": 6.5,
      "iron": "45%"
    },
    {
      "name": "Donut",
      "calories": 452,
      "fat": 25.0,
      "carbs": 51,
      "protein": 4.9,
      "iron": "22%"
    },
    {
      "name": "KitKat",
      "calories": 518,
      "fat": 26.0,
      "carbs": 65,
      "protein": 7,
      "iron": "6%"
    }
  ]
}

```

```Table.vue
<template>
  <v-data-table :headers="headers" :items="desserts" item-key="name" class="elevation-1 pa-6">
    <template v-slot:top>

      <v-container fluid>
        <v-row>

          <v-col cols="4">
            <v-row class="pa-6">
              <!-- Filter for Desert -->
              <v-text-field
                v-model="dessertFilterValue"
                type="text"
                label="Desert Name">
              </v-text-field>
            </v-row>
          </v-col>

          <v-col cols="4">
            <v-row class="pa-6">
              <!-- Filter for Calories -->
              <v-select
                :items="caloriesList"
                v-model="caloriesFilterValue"
                label="Calories"
              ></v-select>

            </v-row>
          </v-col>

          <v-col cols="4">
            <v-row class="pa-6">
              <!-- Filter for Fat-->
              <v-text-field
                v-model="FatFilterValue"
                type="text"
                label="Fat">
              </v-text-field>
            </v-row>
          </v-col>

        </v-row>
      </v-container>

    </template>
  </v-data-table>
</template>

<script>
  // Table info.
  import tableData from './sampleDataTable';
  export default {
    name: 'Table',
    data() {
      return {
        // We need some values for our select.
        caloriesList: [
          {text: "All", value: null},
          {text: "Only 237", value: 237},
          {text: "Only 305", value: 305},
        ],
        // Filter models.
        dessertFilterValue: '',
        FatFilterValue: '',
        caloriesFilterValue: null,
        // Table data.
        desserts: tableData.data,
      }
    },
    computed: {
      headers() {
        return [
          {
            text: 'Dessert (100g serving)',
            align: 'left',
            sortable: false,
            value: 'name',
            filter: this.nameFilter,
          },
          {
            text: 'Calories',
            value: 'calories',
            filter: this.caloriesFilter,
          },
          {
            text: 'Fat (g)',
            value: 'fat',
            filter: this.FatFilter,
          },
          {text: 'Carbs (g)', value: 'carbs'},
          {text: 'Protein (g)', value: 'protein'},
          {text: 'Iron (%)', value: 'iron'},
        ]
      },
    },
    methods: {
      nameFilter(value) {
        // this.dessertFilterValue がなければフィルタリングしない
        if (!this.dessertFilterValue) {
          return true
        }
        return value.toLowerCase().includes(this.dessertFilterValue.toLowerCase());
      },
      caloriesFilter(value) {
        if (!this.caloriesFilterValue) {
          return true
        }
        return value === this.caloriesFilterValue
      },
      FatFilter(value) {
        if(!this.FatFilterValue) {
          return true
        }
        return value === parseInt(this.FatFilterValue)
      }
    }
  }
</script>
```

上記のコードは、

Vuetifyの`<v-text-field>`を使用して、

```vue
<v-text-field
  v-model="dessertFilterValue"
  type="text"
  label="Desert Name">
</v-text-field>
```
のようにv-modelを定義する。
また下記のように、Vue.jsの`data`プロパティにv-modelの初期値を定義する。

```javascript
dessertFilterValue: '',
FatFilterValue: '',
caloriesFilterValue: null,
```

そして、computed() の headers() の各テーブルの項目内で`filter`プロパティを定義する。

```javascript
computed: {
      headers() {
        return [
          {
            text: 'Dessert (100g serving)',
            align: 'left',
            sortable: false,
            value: 'name',
            filter: this.nameFilter,
          },
          {
            text: 'Calories',
            value: 'calories',
            filter: this.caloriesFilter,
          },
          {
            text: 'Fat (g)',
            value: 'fat',
            filter: this.FatFilter,
          },
          {text: 'Carbs (g)', value: 'carbs'},
          {text: 'Protein (g)', value: 'protein'},
          {text: 'Iron (%)', value: 'iron'},
        ]
      },
   },
```

最後に、methodsの各関数でフィルタリングすることでテーブルの表示を切り替えることができる。
例えば`Desert Name`でフィルタリングをかけたい場合、

```javascript
nameFilter(value) {
        // this.dessertFilterValue がなければフィルタリングしない
        if (!this.dessertFilterValue) {
          return true
        }
        return value.toLowerCase().includes(this.dessertFilterValue.toLowerCase());
      },
```
とすることで、v-modelが変化するたびに動的にテーブルの表示を切り替えることが可能になる。

## 結論

**簡潔かつシンプルにフィルタリングできるVuetifyは凄い！！！**