0

This is my custom checkbox component:

<template>
  <label class="checkbox">
    <input
      type="checkbox"
      :checked="isChecked"
      @change="change"
    >
    <span />
    <slot />
  </label>
</template>

<script>
export default {
  name: 'Checkbox',
  model: {
    prop: 'selectedValues',
    event: 'change'
  },
  props: {
    value: {
      type: String,
      required: true
    },
    selectedValues: {
      type: Array,
      default: null
    },
    checked: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    isChecked() {
      return this.selectedValues.includes(this.value);
    }
  },
  created() {
    if(this.checked) {
      const selectedValues = this.selectedValues;
      selectedValues.push(this.value);
      this.$emit('change', selectedValues);
    }
  },
  methods: {
    change() {
      const selectedValues = Array.from(this.selectedValues).slice();
      const found = selectedValues.indexOf(this.value);
      if (found !== -1) {
        selectedValues.splice(found, 1);
      } else {
        selectedValues.push(this.value);
      }
      this.$emit('change', selectedValues);
    }
  }
};
</script>

<style lang="scss">
label.checkbox {
  position: relative;
  user-select: none;
  display: inline-flex;
  cursor: pointer;
  input {
    display: none;
    &:checked ~ span {
      background: #EEE;
      &:after {
        visibility: visible;
      }
    }
  }
  span {
    width: 25px;
    height: 25px;
    border: 1px solid #EEE;
    display: inline-block;
    transition: all linear 0.3s;
    margin-right: 5px;
    &:after {
      content: "";
      position: absolute;
      top: 3px;
      left: 9px;
      border-bottom: 3px solid #FFF;
      border-right: 3px solid #FFF;
      height: 13px;
      width: 5px;
      transform: rotate(45deg);
      visibility: hidden;
    }
  }
}
</style>

Inside my form:

      <Checkbox
        v-model="selectedBrands"
        value="bmw"
        checked
      >
        BMW
      </Checkbox>
      <Checkbox
        v-model="selectedBrands"
        value="audi"
        checked
      >
        Audi
      </Checkbox>
      <Checkbox
        v-model="selectedBrands"
        value="mazda"
      >
        Mazda
      </Checkbox>

  computed: {
    selectedBrands: {
      get() {
        return this.$store.state.selectedBrands;
      },
      set(value) {
        this.$store.commit('setSelectedBrands', {selectedBrands: value});
      }
    }
  }

Vuex store:

export default new Vuex.Store(
  {
    strict: process.env.NODE_ENV !== 'production',

    state: {
      selectedBrands: []
    },
    mutations: {
      setSelectedBrands(state, payload) {
        state.selectedBrands = payload.selectedBrands;
      },
    }
  });

This actually works, but I get vuex error: Error: [vuex] do not mutate vuex store state outside mutation handlers.

However I can change the created() hook in my checkbox component like this:

  created() {
    if(this.checked) {
      const selectedValues = Array.from(this.selectedValues).slice();
      selectedValues.push(this.value);
      this.$emit('change', selectedValues);
    }
  }

The vuex error will go away, but only the last checkbox component (with checked prop) will be checked (in this example Audi). My guess is this happens, because components are rendering asynchronically? Would be happy to hear correct explanation.

My goal is to create a custom checkbox component that supports multiple checkbox v-model array binding (using vuex!) + setting the initial checked state.

I've spent many hours trying to figure out the proper solution. Will be very thankful for your time and help! Thank you in advance!

1
  • Let's figure out, Ping me on skype: syed_haroon Commented Jul 7, 2020 at 14:13

1 Answer 1

1

Your approach will not work - you want the custom checkbox to modify its v-model based on its checked property. But all checkboxes will see the same (empty) selectedValues from the store at the time they emit the change event in created hook. So they will all emit arrays with a single value - their own value. The end result will be that only the last checkbox will become selected - since the computed setter in the parent will be called only after all checkboxes have been created.

If you want to get rid of the mutation error and your checkboxes still working - you should not rely on the checked prop to set their initial value but only rely on the v-model. Therefore, if you want to set them all checked initially - set the Vuex state in your parent component.

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

1 Comment

Thanks for your help, but only the last checkbox with checked prop will be checked initially.

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.