panthro
panthro

Reputation: 24061

Binding an object from checkboxes

I need to bind an object from checkboxes, and in this example, a checkbox is its own component:

<input type="checkbox" :value="option.id" v-model="computedChecked">

Here's my data and computed:

data() {
    return {
        id: 1,
        title: 'test title',
        checked: {
            'users': {
            },
        },
    }
},

computed: {
    computedChecked: {
        get () {
            return this.checked['users'][what here ??];
        },
        set (value) {
            this.checked['users'][value] = {
                'id': this.id,
                'title': this.title,
            }
        }
    },
    ....

The above example is a little rough, but it should show you the idea of what I am trying to achieve:

  1. Check checkbox, assign an object to its binding.
  2. Uncheck and binding is gone.

I can't seem to get the binding to worth though.

Upvotes: 0

Views: 70

Answers (1)

Arie Xiao
Arie Xiao

Reputation: 14082

I assume you want computedChecked to act like an Array, because if it is a Boolean set, it will receive true / false on check / uncheck of the checkbox, and it should be easy to handle the change.

When v-model of a checkbox input is an array, Vue.js expects the array values to stay in sync with the checked status, and on check / uncheck it will assign a fresh array copy of the current checked values, iff:

  • The current model array contains the target value, and it's unchecked in the event
  • The current model array does not contain the target value, and it's checked in the event

So in order for your example to work, you need to set up your setter so that every time the check status changes, we can get the latest state from the getter.

Here's a reference implementation:

export default {
  name: 'CheckBoxExample',
  data () {
    return {
      id: 1,
      title: 'test title',
      checked: {
        users: {}
      }
    }
  },
  computed: {
    computedChecked: {
      get () {
        return Object.getOwnPropertyNames(this.checked.users).filter(p => !/^__/.test(p))
      },
      set (value) {
        let current = Object.getOwnPropertyNames(this.checked.users).filter(p => !/^__/.test(p))
        // calculate the difference
        let toAdd = []
        let toRemove = []
        for (let name of value) {
          if (current.indexOf(name) < 0) {
            toAdd.push(name)
          }
        }
        for (let name of current) {
          if (value.indexOf(name) < 0) {
            toRemove.push(name)
          }
        }
        for (let name of toRemove) {
          var obj = Object.assign({}, this.checked.users)
          delete obj[name]
          // we need to update users otherwise the getter won't react on the change
          this.checked.users = obj
        }
        for (let name of toAdd) {
          // update the users so that getter will react on the change
          this.checked.users = Object.assign({}, this.checked.users, {
            [name]: {
              'id': this.id,
              'title': this.title
            }
          })
        }
        console.log('current', current, 'value', value, 'add', toAdd, 'remove', toRemove, 'model', this.checked.users)
      }
    }
  }
}

Upvotes: 1

Related Questions