【Vue】チェックボックスを選択してもONのまま留める方法

# はじめに
わかってしまえば簡単なことなのだが、
当時は解決に時間を要したので備忘録として残します。

# やりたきこと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122485/b1123b76-893a-ca7c-32d8-51bcbce07702.png)


こんな感じのテーブルにて
チェックボックスをOFFにした際、確認ダイアログを表示する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122485/7dbc6c66-605f-ac3e-ed74-0b77aaf952d2.png)

OKならそのままチェックボックスはOFFに、
キャンセルならチェックボックスはONのままに留めたい。

## 詰まったこと
確認ダイアログでキャンセルを選択してもチェックボックスがOFFになってしまう。

キャンセルしてもOFFになってしまう問題が出ていた時の実装が↓こちら。

### 実装(html)

```html
    <div id="app">
        <table>
            <thead>
                <tr>
                    <th>メールアドレス</th>
                    <th>ユーザ名</th>
                    <th>表示</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="(item, index) in mailList" :key="item.email">
                    <td>{{item.email}}</td>
                    <td>{{item.name}}</td>
                    <td>
                        <input type="checkbox" name="checkbox" v-model="item.dispFlg" @click="onClick(index)">
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
```

### 実装(javascript)

```javascript
        const app = new Vue({
            el: '#app',
            data: {
                mailList: [
                    {
                        email: 'sample_a@gmail.com',
                        name: 'Alice',
                        dispFlg: true
                    },
                    {
                        email: 'sample_b@gmail.com',
                        name: 'Bake',
                        dispFlg: true
                    },
                    {
                        email: 'sample_c@gmail.com',
                        name: 'Cassandra',
                        dispFlg: true
                    },
                    {
                        email: 'sample_d@gmail.com',
                        name: 'Dick',
                        dispFlg: true
                    },
                    {
                        email: 'sample_e@gmail.com',
                        name: 'Ellie',
                        dispFlg: true
                    }
                ]
            },
            methods: {
                onClick(index) {
                    if(this.mailList[index].dispFlg) {
                        const result = confirm(this.mailList[index].name + " のメールアドレスを無効にしますか？");
                        if (!result) {
                            this.mailList[index].dispFlg = true;
                        }
                    }
                }
            }
        })
```

# イケてないところ
キャンセルだった場合に、
`this.mailList[index].dispFlg = true;` 
とすることでチェックボックスをONに戻そうとした。

しかし、これは[公式ドキュメント](https://jp.vuejs.org/v2/guide/reactivity.html) にも書かれている通り、
配列に対してインデックスと一緒にアイテムを直接セットしてもVueは変更を検知できません。

結果、画面にはチェックボックスがOFFの状態で表示されてしまいます。

## 対処方法１：preventDefaultを使う
[preventDefault](https://developer.mozilla.org/ja/docs/Web/API/Event/preventDefault) はイベントの伝搬を止めます。

### 実装(html)

```html
                <tr v-for="(item, index) in mailList" :key="item.email">
                    <td>{{item.email}}</td>
                    <td>{{item.name}}</td>
                    <td>
                        <input type="checkbox" name="checkbox" v-model="item.dispFlg" @click="e => onClick(e,index)">
                    </td>
                </tr>
```

indexと一緒にEventオブジェクトもパラメータで渡します。

### 実装(javascript)

```javascript
            methods: {
                onClick(e,index) {
                    if(this.mailList[index].dispFlg) {
                        const result = confirm(this.mailList[index].name + " のメールアドレスを無効にしますか？");
                        if (!result) {
                            e.preventDefault();
                        }
                    }
                }
            }
```

`preventDefault()` でイベントの伝搬を止めることで、チェックボックスのON⇔OFFが切り替わらなくなります。

ただ、当時は実装上の都合により `preventDefault()` を使えなかったので頭を悩ますことになりました・・・（子コンポーネントから親コンポーネントにEventを渡せなかった）

## 対処方法２：key属性を与えて更新する
[key](https://jp.vuejs.org/v2/api/#key) は更新前後のデータの差分抽出にVue内部で使われている特殊な属性です。

### 実装(html)

```html
    <div id="app">
        <table>
            <thead>
                <tr>
                    <th>メールアドレス</th>
                    <th>ユーザ名</th>
                    <th>表示</th>
                </tr>
            </thead>
            <tbody :key="updateKey">
                <tr v-for="(item, index) in mailList" :key="item.email">
                    <td>{{item.email}}</td>
                    <td>{{item.name}}</td>
                    <td>
                        <input type="checkbox" name="checkbox" v-model="item.dispFlg" @click="onClick(index)">
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
```

`tbody` に対してkeyを付与します。

### 実装(javascript)

```javascript
        const app = new Vue({
            el: '#app',
            data: {
                mailList: [
                    // :
                    // 省略
                    // :
                ],
                updateKey: true
            },
            methods: {
                onClick(index) {
                    if(this.mailList[index].dispFlg) {
                        const result = confirm(this.mailList[index].name + " のメールアドレスを無効にしますか？");
                        if (!result) {
                            this.updateKey = !this.updateKey;
                        }
                    }
                }
            }
```

新たに `updateKey` というデータを用意。
キャンセルの場合、 `updateKey` の値を更新することでVueに `<tbody>` 配下の再描画を促します。

結果、チェックボックスはOFFのままとなります。

# まとめ
ユーザの操作に対してVueインスタンスで保持しているデータを更新させたくない場合、
`preventDefault` または `key` を試してみよう。