はじめに
チェックボックスをv-model
で配列をバインドする場合ではなく、form submitにおける:checked
とvalue
を利用した際にハマったことです。
背景
今回このように、「全て」というチェックボックスを置き、全選択・全解除機能を実装しようとしました。(後々の説明でわかりやすいように、Booleanを括弧内に表示しています)
実装はこちら↓↓
<template>
...
<form>
...
<div>
<input
type="checkbox"
:value="checkAll"
:checked="checkAll"
label="全て"
@change="handleCheckAll"
>
</div>
<input
v-for="item in items"
:key="item.id"
:value="item.id"
label="item.name"
@change="handleCheckbox"
>
...
</form>
...
</template>
<script>
...
data() {
return {
items: [
{ id: 1, name: '木工事' }
...
],
obj: {
1: true,
...
},
checkAll: true
}
},
...
methods: {
handleCheckAll() { // 全選択・全解除
this.checkAll = !this.checkAll
for (const key in this.obj) {
this.$set(this.obj, key, this.checkAll)
}
},
handleCheckbox(id) { // 個別のチェックボックス
this.obj[id] = !this.obj[id]
const values = object.values(this.obj) // オブジェクトからvalueの配列を作成
this.checkAll = values.every(value => value) // 全てtrueかどうか
},
...
}
...
</script>
(本来はカスタムコンポーネントやVuexデータを利用していますが、サンプルコードとして大幅に省略しております)
しかしこれでは、うまく行きませんでした。
2つ目のチェックボックスをクリックしても、Booleanが切り替わらないのです。それにより、全選択・全解除してもうまく全て切り替わってくれません。console.log
でobj
を見るとうまく状態が切り替わっていますが、画面上はこのような動作になっています。
なぜか
公式ページ(リアクティブの探求)に書いてありました。
モダンな JavaScript の限界(そして Object.observe の断念)のため、Vue.js はプロパティの追加または削除を検出できません。
(中略)
既存のオブジェクトに複数のプロパティを割り当てたいということがあるかもしれません。例えば、Object.assign() や _.extend() を使って。しかし、オブジェクトに追加された新しいプロパティは変更をトリガしません。このような場合は、元のオブジェクトとミックスインオブジェクトの両方のプロパティを持つ新たなオブジェクトを作成してください
ふむふむ。
単にオブジェクトのプロパティを変更しただけでは、それを検出できないそうです。
ということで修正した
チェックボックスをクリックした時に、対象の中身を書き換えるのではなく、オブジェクトをコピーしてしまい、それのプロパティを変更。そして元のdataに上書きするということをします。
<script>
...
methods: {
...
handleCheckbox(id) {
const newObj = JSON.parse(JSON.stringify(this.obj)) // ディープコピー
newObj[id] = !newObj[id] // コピーしたものの値を変更
this.obj = newObj // 書き換える
...
},
...
}
...
</script>
さいごに
Vueにはリアクティブの探求で知らないとハマるケースがところどころあると思います。もしオブジェクトを扱っていて、あれ、おかしいな?というケースに出くわしたら、一度今回のような箇所を疑ってみると解決するかもしれません。