5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.jsでのチェックボックス全選択の動きが変?リアクティブの検出

Posted at

はじめに

チェックボックスをv-modelで配列をバインドする場合ではなく、form submitにおける:checkedvalueを利用した際にハマったことです。

背景

今回このように、「全て」というチェックボックスを置き、全選択・全解除機能を実装しようとしました。(後々の説明でわかりやすいように、Booleanを括弧内に表示しています)
スクリーンショット 2019-10-24 17.19.10.png

実装はこちら↓↓


<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データを利用していますが、サンプルコードとして大幅に省略しております)

しかしこれでは、うまく行きませんでした。
bad.gif
2つ目のチェックボックスをクリックしても、Booleanが切り替わらないのです。それにより、全選択・全解除してもうまく全て切り替わってくれません。console.logobjを見るとうまく状態が切り替わっていますが、画面上はこのような動作になっています。

なぜか

公式ページ(リアクティブの探求)に書いてありました。

モダンな 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>

すると!
期待していた挙動が確認できました。
good.gif

さいごに

Vueにはリアクティブの探求で知らないとハマるケースがところどころあると思います。もしオブジェクトを扱っていて、あれ、おかしいな?というケースに出くわしたら、一度今回のような箇所を疑ってみると解決するかもしれません。

5
3
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
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?