4
1

More than 3 years have passed since last update.

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

Posted at

はじめに

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

やりたきこと

image.png

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

image.png

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

詰まったこと

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

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

実装(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)

        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に戻そうとした。

しかし、これは公式ドキュメント にも書かれている通り、
配列に対してインデックスと一緒にアイテムを直接セットしてもVueは変更を検知できません。

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

対処方法1:preventDefaultを使う

preventDefault はイベントの伝搬を止めます。

実装(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)

            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を渡せなかった)

対処方法2:key属性を与えて更新する

key は更新前後のデータの差分抽出にVue内部で使われている特殊な属性です。

実装(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)

        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 を試してみよう。

4
1
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
4
1