Search code examples
vuejs3vue-component

How Can I let 2 child components update the same parent property?


I have some products that I want to filter based on their category using checkboxes.

To do so, I have a parent component which passes the possible categories (e.g. A, B and C) to the child and keeps track of the checked/selected categories:

Parent.vue

<template>
  <FilterCheckboxItem
    ref="Category"
    name="Category"
    :FilterOptions="CategoryOptions"
    v-model="FilteredCategories"
  />
</template>

<script>


export default {
    data(){
        return {
          FilteredCategories: [],
          CategoryOptions: ["A","B","C"]
         }
    }
</script>

And the child component looks like this (simplified):

FilterCheckboxItem.vue

<template>
<div
  v-for="FilterOption in FilterOptions"
>
  <input
    type="checkbox"
    :value="FilterOption"
    v-model="checked"
  />
</div>
</template>

<script>

export default {
    props: {
     FilterOptions: {
      type: Array,
    },
    modelValue: {
      type: Array,
    },
 },

  emits: ['update:modelValue'],


  setup(props, { emit }) {
    const checked = ref([]);
    watch(checked, (newVal) => {
      emit('update:modelValue', newVal);
    });
    return { emit, checked };
  },

</script>

This works as intended.

However, when I would like to add the same child component again (for example 1 filterbutton in the normal menu and 1 in the mobile-menu) the arrays from the 2 childs are not aligned anymore. For example, I select Category "A" on the first child component, then the second child component is not updated properly (Category "A") will not be checked.

How can I make sure that the whenever I change something in the first child-component it updates the parent component but also makes sure that the second child-component is updated properly?

I would expect this to work since they are referred to the same v-model property.

Vue Playground


Solution

  • The v-model in the parent, which exists in the Child component as modelValue, is not the same v-model used on the actual inputs which is checked. When checked update you use a watcher to update the parent v-model. There's no equivalent code to update checked when the v-model updates, but there doesn't need to be. Just use modelValue as your v-model on the input. No need to have checked or a watcher at all.

    FilterCheckboxItem.vue

    <template>
    <div
      v-for="(FilterOption, index) in FilterOptions"
    > 
      <label for="lname">{{FilterOption}}</label>   
      <input
        type="checkbox"
        :value="FilterOption"
        v-model="modelValue[index]"
      />
    </div>
    </template>
    
    <script>
    export default {
        props: {
         FilterOptions: {
          type: Array,
        },
        modelValue: {
          type: Array,
        },
     },
    }
    </script>
    

    Vue Playground example