はじめに
わかってしまえば簡単なことなのだが、
当時は解決に時間を要したので備忘録として残します。
やりたきこと
こんな感じのテーブルにて
チェックボックスをOFFにした際、確認ダイアログを表示する。
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
を試してみよう。