Search code examples
javascripthtmlvue.jsvuejs2bootstrap-vue

How to make parent tag and child tag have onclick without repetition on a b-form-checkbox


I have a li tag with some different child tags which also includes a b-form-checkbox. I want the check box to be working when I click anywhere on li tag and which also includes on the checkbox. when I click anywhere in li it works fine but when I click on b-form-checkbox the onclick event is called 2 times and checkbox simply go check and uncheck on 1 click. I have tried many ways like .self but then v-on:click on b-form-checkbox don't work.

li tag code below including the b-form-checkbox and template tag in which they are present.

<template v-for="(product, index) in products">
          <div
            class="col-12 col-md-6"
            :key="product.id"
            v-if="!product.responded"
          >
            <li
              class="products-search__list-item"
              v-on:click.self="toggleProduct(index)"
            >
              <div v-on:click.self="toggleProduct(index)" class="products-search__image">
                <img :src="product.img" alt="product.id" />
              </div>
              <div v-on:click.self="toggleProduct(index)" class="products-search__details">
                <span>{{ product.amount }}x {{ product.name }}</span>
                <span v-if="$func.regionCheckGLobal()"
                  >$ {{ product.unitPrice }} / unit</span
                >
                <span v-else>PKR {{ product.unitPrice }} / unit</span>
              </div>
              <div v-on:click.self="toggleProduct(index)" class="products-search__check-box" >
                <b-form-checkbox v-on:click.self="toggleProduct(index)"  v-model="product.checked" > </b-form-checkbox>
              </div>
            </li>
          </div>
        </template>

data() in script part related variable below

 return {   
      products: [],

    };

methods: code in script part of vue related to onclick

 toggleProduct(index) {
   this.products[index].checked = !this.products[index].checked;
}

Solution

  • <template>
      <div class="container">
        <div v-for="(product, index) in products" :key="index">
          <div class="col-12 col-md-6" :key="product.id" v-if="!product.responded">
            <li class="products-search__list-item" @click="toggleProduct(index)">
              <div class="products-search__image">
                <img :src="product.img" alt="product.id" />
              </div>
              <div class="products-search__details">
                <span>{{ product.amount }}x {{ product.name }}</span>
                <span>$ {{ product.unitPrice }} / unit</span>
              </div>
              <div class="products-search__check-box">
                <b-form-checkbox v-model="product.checked" />
              </div>
            </li>
          </div>
        </div>
      </div>
    </template>

    This is how I modified your template, instead of the input type checkbox you can have the b-form-checkbox, it should behave the same way as the input in my example. Here is the Code Sandbox link to what I did to make it work, i believe this is what you needed, and now i will explain in more detail:

    Since you had all those click listeners on all of the child components of the li, your click on areas where there was a click listener on for example the checkbox and the div the checkbox was in and the div that div with checkbox was in the click event bubbled up to the outermost component, in this case you either put a click listener on the outermost component you want to trigger the action with our every subcompomponent (dont do nested handlers please) that you want the click to be triggered on.

    In conclusion DO NOT do nested event listeners!

    Hope it helped!

    UPDATE: Check your code again for differences between my code and yours, added bootstrap-vue for you, and used b-form-checkbox in the codesandbox example! This works as you wanted it to :D