Chris
Chris

Reputation: 3486

@click on checkbox add/remove data

I currently have the following scenario:

I have multiple checkboxes, once any is clicked, the value of it will get added to an array. If the checkbox is unchecked then the item needs to be removed out of the array again.

selectAddOn(addOnId) {
    if (! this.selectedAddOns.includes(addOnId)) {
        this.selectedAddOns.push(addOnId);
    }
}

The following works and it adds them to my selectedAddOns[]. But when the checkbox is checked again, it is not removed. Sure, I could just use else, but...

Unfortunately, the browser behavior is when you click on a <label>, a click event will automatically be triggered on the <input>, so the outer div receives 2 events, one from label, one from input. I am aware that I can work around this by adding @click.prevent on the <label>, but this then will not add my custom checkbox styles.

<div @click="selectAddOn(index)" class="col-6" v-for="(addOn, index) in categories[categoryId].addOns">
    <label class="custom-control custom-checkbox">
        <input type="checkbox" class="custom-control-input">
        <span class="custom-control-indicator"></span>
        <span class="custom-control-description">{{ addOn.name }} (+ ${{ addOn.price }})</span>
    </label>
</div>

Any idea on how I can work around this scenario?

Upvotes: 1

Views: 3919

Answers (2)

Roy J
Roy J

Reputation: 43899

This is a built-in behavior of v-model when used with an array on multiple checkboxes. You don't need a click handler. (Code shamelessly lifted from Bert's answer.)

console.clear()

new Vue({
  el: "#app",
  data:{
    selectedAddOns:[],
    categories:[
      {
        addOns:[
          {name: "AddOn One", price: 10},
          {name: "AddOn two", price: 20},
          {name: "AddOn Three", price: 30},
        ]
      },

    ],
    categoryId: 0
  }
})
<script src="https://unpkg.com/[email protected]"></script>
<div id="app">
  Selected Addons: {{selectedAddOns}}
  <div class="col-6" v-for="addOn, index in categories[categoryId].addOns">
    <label class="custom-control custom-checkbox" >
        <input type="checkbox" class="custom-control-input" :value="index" v-model="selectedAddOns" >
        <span class="custom-control-indicator"></span>
        <span class="custom-control-description">{{ addOn.name }} (+ ${{ addOn.price }})</span>
    </label>
</div>
</div>

Upvotes: 4

Bert
Bert

Reputation: 82489

Put the click event handler on the input.

console.clear()

new Vue({
  el: "#app",
  data:{
    selectedAddOns:[],
    categories:[
      {
        addOns:[
          {name: "AddOn One", price: 10},
          {name: "AddOn two", price: 20},
          {name: "AddOn Three", price: 30},
        ]
      },

    ],
    categoryId: 0
  },
  methods:{
    selectAddOn(addOnId) {
      let index = this.selectedAddOns.findIndex(a => a === addOnId)
      if (index >= 0) 
        this.selectedAddOns.splice(index, 1)
      else 
        this.selectedAddOns.push(addOnId);
    }
  }
})
<script src="https://unpkg.com/[email protected]"></script>
<div id="app">
  Selected Addons: {{selectedAddOns}}
  <div class="col-6" v-for="(addOn, index) in categories[categoryId].addOns">
    <label class="custom-control custom-checkbox" >
        <input type="checkbox" class="custom-control-input" @click="selectAddOn(index)" >
        <span class="custom-control-indicator"></span>
        <span class="custom-control-description">{{ addOn.name }} (+ ${{ addOn.price }})</span>
    </label>
</div>
</div>

Upvotes: 1

Related Questions