1

Desperately in need of your help guys.

So basically I have a custom checkbox component whit a v-model. I use a v-for loop on the component to display checkboxes with the names from the array. In the parent component I have two columns Available and Selected. The idea is that if I check one of the boxes in the Available column it should appear on the Selected column. The problem is that it displays letter by letter and not the full name.

I am able to achieve the desired result without having a checkbox component, but since I will be needing checkboxes a lot throught my project I want to have a component for it.

Please follow the link for the code: CodeSandBox

Dont mind the difference in styling.

The problem:

Problem

The desired outcome:

Desired outcome

2 Answers 2

4

There are two problems. The first problem is, that you have your v-model set to v-model="filter.filterCollection", so a checkbox you select will be stored into the array as a string and if you select another checkbox the string gets overwritten. The second problem is, that you call that stored string as an array. That causes, that your string, which is an array of letters, will be rendered for each letter. So 'Number' is like ["N", "u", "m", "b", "e", "r"].

To solve your problem, you need to store every selection with its own reference in your v-model. To cover your needs of correct listing and correct deleting you need to apply the following changes:

Your checkbox loop

<Checkbox
   v-for="(item, index) in items"
   :key="item.id"
   :label="item.name"
   :id="index"
   :isChecked="isChecked(index)" // this is new
   @remove-selected-filter="removeSelectedFilter" // This is new
   :modelValue="item.name"
   v-model="filter.filterCollection[index]" // Change this
/>

Your v-model

filter: {
        filterCollection: {} // Object instead of array
      }

Methods in FilterPopUp.vue

methods: {
    removeSelectedFilter(index) {
      delete this.filter.filterCollection[index];
    },
    isChecked(index) {
      return !!this.filter.filterCollection[index];
    }
  }

Your Checkbox.vue:

<template>
  <label>
    <p>{{ label }}</p>
    <input
      type="checkbox"
      :id="id"
      :value="modelValue"
      :checked="isChecked"
      @change="emitUncheck($event.target.checked)"
      @input="$emit('update:modelValue', $event.target.value)"
    />
    <span class="checkmark"></span>
  </label>
</template>

<script>
export default {
  name: "Checkbox",
  props: {
    modelValue: { type: String, default: "" },
    isChecked: Boolean,
    label: { type: String },
    value: { type: Array },
    id: { type: Number },
  },
  methods: {
    emitUncheck(event) {
      if(!event){
        this.$emit('remove-selected-filter', this.id);
      }
    }
  }
};
</script>

This should now display your items properly, delete the items properly and unselect the checkboxes after deleting the items.

Sign up to request clarification or add additional context in comments.

5 Comments

And what about removing items? :-)
Thank you very much for your help, but now I have another problem if I click on the remove item SVG on the selected column it removes the item but the box still stays checked in the available column. Any idea?
@Fersek As the answer of Tolbxela also causes that the checkbox still is checked, I edited my answer and this should work as you need.
@StevenSiebert thank you removing with the SVG works now too, but now if i check and uncheck the BOX the item still stays on the selected column.
@Fersek Did a change on Checkbox.vue and checkbox loop in my answer. It should work now with deleting and unchecking. Let me know.
2

StevenSiebert has correctly pointed to your errors.

But his solution is not complete, since the filters will not be removed from the collection when you uncheck one of them.

Here is my complete solution of your checkbox working as expected:

Checkbox.vue

<template>
  <label>
    <p>{{ label }}</p>
    <input
      type="checkbox"
      :id="id"
      v-model="checked"
      @change="$emit('change', { id: this.id, checked: this.checked})"
    />
    <span class="checkmark"></span>
  </label>
</template>

<script>
export default {
  name: "Checkbox",
  props: {
    modelValue: { type: Boolean, default: false },
    label: { type: String },
    id: { type: Number },
  },
  emits: ["change"],
  data() {
    return {
      checked: this.modelValue
    };
  }
};
</script>

FilterPopUp.vue

<template>
   ...
       <Checkbox
          v-for="(item, index) in items"
          :key="index"
          :label="item.name"
          :id="index"
          @change="onChange"
        />
  ...            

</template>

<script>
...
methods: {
    removeSelectedFilter(index) {
      this.filter.filterCollection.splice(index, 1);
    },
    onChange(args) {
      const {id, checked} = args;
      const item = this.items[id].name;
      if (checked) {
        if (this.filter.filterCollection.indexOf(item) < 0) {
         this.filter.filterCollection.push(item);
        }
      } else {
        this.filter.filterCollection = this.filter.filterCollection.filter( i=> i != item);
      }
    },
  },
...

Here is the working CodeSandbox:

https://codesandbox.io/s/pensive-shadow-ygvzb?file=/src/components/Checkbox.vue

Sure, there are many ways to do it. If somebody has a nicer and shorter way to do it, please post your solution. It will be interesting to look at it.

2 Comments

This causes that the checkbox still is checked after you delete the item from the list.
Yes, you are right. I didn't want to do all the work myself. :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.