【Vue.js】指定した値と一致するレコードのチェックボックスを外す

# 概要
v-modelを使ったテーブルで、ユーザが指定した値と一致するレコードのチェックボックスを外す方法をまとめました。
画面イメージは以下の通りです。
* 入力欄に名称を入力
* 「チェック解除」ボタンを押下すると、入力欄の名称と一致するレコードを検索
* 一致するレコードの「チェック」ボタンがON(赤色の状態)の場合、OFF(灰色の状態)にする

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/a6a4cfc2-6e48-50de-bb27-05cf5df4b81c.png)
↓
![1-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/f826f93b-1e40-8667-e460-d23dc8e7c16b.png)


画面を構成しているのは以下の3つのファイルです。
　チェックボタンコンポーネント：AppCheckButton.vue
　テーブルコンポーネント：Table.vue
　ページ：TestSetting.vue


今回はスタイルの説明は割愛させていただきます。
# 各ファイルのコード
チェックボックスを外す機能を実装する前に、各ファイルのコードを軽く説明します。

### チェックボタンコンポーネント(AppCheckButton.vue)
```html:AppCheckButton.vue
<template>
    <label>
      <input type="checkbox" name="checkbox" class="checkbox-input" v-model="selected" :value="value">
      <span class="rounded_btn">{{ label }}</span>
    </label>
</template>
<script>
  export default {
    name: 'AppCheckButton',
    data () {
      return {
        selected: [],
      };
    },
    props: {
      label: String,
      value: [Number,String]
    },
</script>
```
inputタグを``type="checkbox"``　``v-model="selected"``　``:value="value"``に設定します。チェックを付けるとselectedにvalueが格納される仕組みです。
selectedはリストで本コンポーネントに定義し、labelとvalueはpropsを使ってTable.vueからデータを取ってくるようにします。

### テーブルコンポーネント(Table.vue)
```html:Table.vue
<template>
    <div class="vt">
        <div class="master-table">
          <table id="vt">
            <thead>
              <tr class="table-header-row">
                <th v-for="(header, index) in headers"
                    v-bind:key="index"
                    :width="header.width"
                    class="table-header-col">
                  {{header.text}}
                </th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="(item, index) in items"
              v-bind:key="item.id" 
              class='table-col'
              >
                <td class="text-field">
                  {{ item.field1 }}
                </td>
                <td>
                  <span class="CheckButton">
                    <AppCheckButton
                    label="チェック"
                    class="notlink"
                    :value="item.field1"
                    ref="check"
                    />
                  </span>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
    </div>
</template>
<script>
import AppCheckButton from "@/components/atoms/Input/AppCheckButton.vue";

export default {
  name: "Table",
  components: {
    AppCheckButton
  },
  data() {
    return {};
  },
  props: {
    headers: {
        type: Array,
        required: true,
    },
    items: {
        type: Array,
        required: true,
    }
  },
</script>
```
TestSetting.vueからheadersとitemsを参照し、ヘッダとボディにそれぞれv-forで格納しています。
先程紹介したAppCheckButton.vueで定義していたlabelには"チェック"という文字列を、valueにはitem.field1のデータを渡しています。
item.field1の具体的なデータは次で紹介するTestSetting.vueに記述されています。


### ページ(TestSetting.vue)
```html:TestSetting.vue
<template>
  <div>
    <ValidationObserver ref="validationObserver">
        <label>
          <input tipe="text"  class="testInput" id="testInput">
          <input type="button" value="チェック解除" class="testButton">
        </label>
            <Table
            ref="table"
            name="名称"
            :headers="NameHeaders"
            :items="NameItems"
            />
    </ValidationObserver>
  </div>
</template>
<script>
import Table from "../organisms/Table.vue";

export default {
  name: "TestSetting",
  components: {
    Table
  },
  data() {
    return {
      NameHeaders: [
        { text: "名称",
          align: "start",
          value: "field1",
          sortable: false,
          width: "40%"
        },
        { text: "操作",
          value: "check",
          sortable: false,
          width: "20%"
        },
      ],
      NameItems: [
        {field1:"test1", NameId:1, id:1},
        {field1:"test2", NameId:2, id:2},
        {field1:"test3", NameId:3, id:3}
      ],
    };
  },
</script>
```
Table.vueをインポートしており、Table.vueのheadersにNameHeaders、itemsにNameItemsを渡しています。
また、「チェック解除」ボタンを押下すると、buttonClick()が発火します。buttonClick()の内容は、入力欄の値を取得し、$refsを用いてTable.vueに登録されているchangeCheck()を発火させるというものです。

# チェックボックスを外す機能の実装
上記で紹介したテーブル画面に、指定した値と一致するレコードのチェックボックスを外す機能を追加していきます。

動作の大まかな流れは以下の通りです。

① 入力欄の値を取得　(TestSetting.vue)
② テーブルの長さ分だけfor文を回す　(Table.vue)
③ 入力欄の値とテーブルのレコードの値を比べ、値が一致した場合、チェックを外す　(AppCheckButton.vue)

### ①入力欄の値を取得　(TestSetting.vue)

入力欄の値を取得するbuttonClick()関数を作成します。@clickで「チェック解除」ボタン押下時に発火するよう紐づけます。また、$refsを使って、②でTable.vue内に定義するchangeCheck()が実行されるよう設定します。

```html:TestSetting.vue
<!-- 省略 -->
        <label>
          <input tipe="text"  class="testInput" id="testInput">
          <input type="button" value="チェック解除" @click="buttonClick" class="testButton">
        </label>
<!-- 省略 -->
<script>
// 省略
  methods: {
      buttonClick() {
        let inputValue = document.getElementById("testInput").value;
        this.$refs.table.changeCheck(inputValue);
      },
  }
</script>

```

### ② テーブルの長さ分だけfor文を回す　(Table.vue)

v-forでテーブルにデータを渡しているため、`` <AppCheckButton/> ``もデータの個数分生成されます。そのためテーブルの長さをthis.$refs.check.lengthで表すことが可能です。
this.$refs.check.length分for文を回し、AppCheckButton.vueで定義するunchecked()に、TestSetting.vueで渡した入力欄の値を渡します。

```html:Table.vue
<!-- 省略 -->
<script>
// 省略
  methods: {
    changeCheck (Value){
      for(var i = 0; i < this.$refs.check.length; i++){
        this.$refs.check[i].unchecked(Value);
      }
    }
  }
</script>
```

### ③ 入力欄の値とテーブルのレコードの値を比べ、値が一致した場合、チェックを外す　(AppCheckButton.vue)

チェックが付いている状態ではselected[]にvalueが格納されており、チェックが外れている状態ではselected[]は空の状態になります。分かりやすい様に下記の``<!-- 追加　-->``部分をAppCheckButton.vueに追加して画面を確認してみましょう。
```AppCheckButton.vue
<template>
    <label>
      <input type="checkbox" name="checkbox" class="checkbox-input" v-model="selected" :value="value"
        >
      <span class="rounded_btn">{{ label }}</span>
      <span class="rounded_btn">{{ selected }}</span> <!-- 追加　-->
    </label>
</template>
```
チェックが外れているときはselectedは空ですが、
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/4def3c76-781e-31fa-b7de-8f7655bc575b.png)
チェックを付けると、、、
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/1689e920-e39c-a8ec-5bf8-e32914d02abf.png)
selectedにvalueが入っています。


つまり、入力欄の値とテーブルの値が一致した際にselectedを空にしてあげればよいという事なので、以下の様なunchecked()関数を作成すれば無事チェックボックスを外す機能が完成します。

```AppCheckButton.vue
<!-- 省略 -->
<script>
// 省略
    methods: {
      unchecked: function(Value){
        if(this.selected[0] == Value){
          this.selected = [];
        }
      }
    }
  }
</script>
```

### 確認
全レコードにチェックを入れ、入力欄に"test3"と入力し、「チェック解除」を押下します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/029571bf-1abe-9d35-420e-5343106ff2de.png)

無事、test3だけチェックが外れました！
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/078037f8-1261-4e2e-afc4-78cef3e8c706.png)

### おまけ
この仕組みを応用すれば、下記の様な機能が作れます。
<br>

削除予定のレコードの「削除」ボタンにチェックを入れる
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/9500df8a-5ae5-16b6-c89d-a675d978062e.png)
表示順を変更・変更内容を保存
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/53788142-bb7d-2321-8e8a-e8c336058457.png)
削除不可の場合
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/c21546ff-fbe9-67f7-e538-446e468eaa0f.png)
「削除」ボタンのチェックが解除される
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3334459/36f9589b-d3c3-db18-6593-bea90625b015.png)

登録・編集・削除ができるマスタテーブルで、削除予定のレコードが削除不可の場合、「削除」チェックがオフになるよう制御しています。
テーブルの表示順が可変の場合でも、今回紹介した仕組みを使えば、レコードを特定することが可能です。

# おわりに
今回は指定した値と一致するレコードのチェックボックスを外す方法を記事にまとめました。
今回の実装を経て、propsや$refsの仕組みなど、Vue.jsの理解をまた少し深められたと感じています。
Vue.jsを用いて似たような機能を実装したい方がいらっしゃれば、参考にしていただけますと幸いです。