Mickaël
Mickaël

Reputation: 124

Check / Uncheck a Checkbox in a child component with vuejs

Context :

I am using VueJS in a project for the first time so I'm potentially not using it the right way.

I have a parent and a child component :

Parent : ResearchProducts.vue : Displays a list of products that can be filtred by their categories. The filters are checkboxes related to the category of the products.

Child : CategoryDisplay.vue : This is the component that handle the view and the methods of a category row.

My Question :

When I click on a category checkbox inside of my child component and if the checkbox is checked, the category is added to a list of filters in the parent. This works.

When I uncheck the checkbox, the category is removed from this list. This works too.

Now I've showed all my categories as buttons in the parent component to make the filters visible more easily for the user. And my problem is that I want this button to uncheck the related checkbox inside the child component when I click on it.

Here is my actual code :

ResearchProducts.vue :

<template>
 <div>
     <template v-for="category in categories">
          <category-display
                 :category="category"
                 :key="'category_'+category.id"
                 :checked="checked"
                 @categorySelected="categorySelected"
               ></category-display>
     </template>
     <button
               v-for="filter in listFilters"
               :key="'filter_'+filter.slug"
               class="btn btn-light btn-sm mr-2 mb-2"
               @click="removeFilter(filter)"
             >
               {{ filter.name }}
      </button>
 </div>
</template>
<script>
export default {

  data() {

    return {

      categories: { // HERE A COLLECTION CONTAINING ALL MY CATEGORIES },

      selectedCategories: [],

      listFilters: []

    };

  },

  methods: {

    categorySelected(category) {

      var newCategory = {

        type: "category",

        slug: category.slug,

        name: category.name

      };

      if (category.checked) {

        if (!this.selectedCategories.includes(category.slug)) {

          this.selectedCategories.push(category.slug);

          this.listFilters.push(newCategory);

        }

      } else {

        if (this.selectedCategories.includes(category.slug)) {

          const index = this.selectedCategories.indexOf(category.slug);

          if (index > -1) {

            this.selectedCategories.splice(index, 1);

          }

        }

        this.listFilters = this.listFilters.filter(function(item) {

          for (var key in newCategory) {

            if (item[key] === undefined || item[key] == newCategory[key])

              return false;

          }

          return true;

        });

      }

    },

    removeFilter(filter) {
     // THERE, I NEED TO UNCHECK THE RELATED CHECKBOX IN CHILD COMPONENT
     this.listFilters = this.listFilters.filter(function(item) {
        for (var key in filter) {
          if (item[key] === undefined || item[key] == filter[key]) return false;
        }
        return true;
      });

    }

  }

};
</script>

CategoryDisplay.vue :

<template>
  <b-form-checkbox-group class="w-100">
    <b-form-checkbox :value="category.slug" class="w-100" @input="selection" v-model="selected" ref="checked">
      {{ category.name }}
      <span class="badge badge-secondary float-right">{{ category.products_count }}</span>
    </b-form-checkbox>
  </b-form-checkbox-group>
</template>

<script>
export default {
  props: {
    category: {
      type: Object,
      required: true
    }
  },

  data() {
    return {
      selected: false
    }
  },

  methods: {
    selection() {
      var response = false;
      if(this.selected.length !== 0){
        response = true;
      }
      this.$emit('categorySelected', {slug: this.category.slug, name: this.category.name, checked: response});
    }
  }
};
</script>

Upvotes: 6

Views: 4596

Answers (1)

AVJT82
AVJT82

Reputation: 73357

Here is a simplified sample for your reference. You can use the sync modifier to achieve a two-way-binding between the parent and child. That together with a computed property in the child with a setter and getter

So passing all categories to child, and sync the selected categories:

<HelloWorld :cats.sync="selectedCategories" :categories="categories"/>

Child component takes the categories, iterates and shows checkboxes. Here we use the computed property, and when a checkbox is clicked, the setter emits the change to the parent:

<label v-for="c in categories" :key="c.id">
  <input v-model="temp" type="checkbox" :value="c">
  {{ c.name }}
</label>

script:

computed: {
  temp: {
    get: function() {
      return this.cats;
    },
    set: function(newValue) {
      this.$emit("update:cats", newValue);
    }
  }
}

And the parent just simply iterates the selectedCategories and as you wish, when a change happens in parent for selectedCategories, child will be automatically aware of when an item is deleted.

Here's a full sample for your reference: SANDBOX

Upvotes: 6

Related Questions