Asked By: Anonymous
I’m building “base” components for my application, mostly for form management. So far I’ve made a custom input, textarea, select, and radio. However, all those components only have “one” value at a time.
For radios and checkboxes, my component builds multiple radios/checkboxes at once, all attached to the same v-model. Howevever, my checkbox component is not working as intended. Instead of storing “all the checked checkboxes” in an array in the v-model, it only registers the last one clicked 🙁.
How can I override the “change” event so that it updates the attached model accordingly, by adding/removing the element from the array?
Here’s my code for the checkbox component:
/**
* @description Component used to display a group of checkboxes
* @author Jordan Kowal
* @date 2019-11-30
* @param name String, name attribute
* @param alignment String, defines item alignment and should be "centered", "left", or "right"
* @param block Boolean, whether items are displayed as "block" or "inline-block"
* @param entries Array, list of objects with the following keys: label, value, active
* @param errors Array, list of error messages
*/
Vue.component("checkbox-group", {
props: {
name: String,
alignment: String,
block: Boolean,
entries: Array,
errors: Array,
},
template:
"<div class='field'>
<p class='help is-danger' v-for='message in errors'>
@{message}
</p>
<div class='control' :class='"has-text-" + alignment'>
<template v-for='entry in entries'>
<label class='checkbox'>
<input
type='checkbox'
:name='name'
:checked='entry.active'
:value='entry.value'
@change='$emit("input", $event.target.value)'
/>
@{entry.label}
</label>
<br v-if='block'>
</template>
</div>
</div>",
});
Exemple of use:
<checkbox-group
name="id"
alignment="centered"
:block="true"
v-model="languageForm.fields.languages"
:entries="languageForm.entries.languages"
:errors="languageForm.errors.languages"
></checkbox-group>
EDIT:
Solution provided by Michal Levý is working perfectly
Solution
Answered By: Anonymous
Take a look what v-model
on custom components does
<mycomponent v-model="data" />
is same as
<mycomponent
v-bind:value="data"
v-on:input="data= $event" />
So if you do $emit("input", $event.target.value)
inside of your component, whatever $event.target.value
is, it replaces your model. But you need an “input” event so the v-model
on your component works. value
emitted just has to be in the same format as a model you are receiving.
On top of that whats the point of having a model and at the same time support entries.active
? It’s clear duplicity. Passed model should be only thing that decides whether checkbox is checked or not….
Do it like this:
Vue.component("checkbox-group", {
props: {
name: String,
alignment: String,
block: Boolean,
entries: Array,
errors: Array,
value: Array
},
data() {
return {
model: this.value
}
},
watch: {
model: function(val) {
this.$emit("input", val)
}
},
template:
"<div class='field'>
<p class='help is-danger' v-for='message in errors'>
@{message}
</p>
<div class='control' :class='"has-text-" + alignment'>
<template v-for='entry in entries'>
<label class='checkbox'>
<input
type='checkbox'
:name='name'
:value='entry.value'
v-model='model'
/>
{{entry.label}}
</label>
<br v-if='block'>
</template>
</div>
</div>"
});
Changes:
- added
value
prop – its set by Vue to a value insidev-model
attribute of your component - introduce
model
in data – variable holds current state of your checkboxes. At the begging it’s initialized fromv-model
- removed
:checked=...
because its a duplicity. Checked state of checkboxes is driven by model - added watcher – every time model inside your component is changed (by
v-model
on checkboxes), emit an event so the parent’s model is updated