As I work on migrating my components to Vue 3, I'm facing confusion with setting the V-model, particularly in a nested component like CheckboxFieldComponent. The initial state of the checkbox should be unchecked as I retrieve a value from the API. Where would be the best place to set the v-model for this nested component?
CheckboxFieldComponent.vue
<template>
<div v-show="isVisible">
<FormCheckbox
v-model="booleanFromStoreConvertedToArray"
:input-value="field.model.itemId"
:title="title"
:id="field.valueField.id"
:class="field.model.cssClass"
:name="field.valueField.name"
:checked="true"
:required="required"
:disabled="!isEnabled"
@input="
onInput(value);
$emit('change');
"
@focus="field.tracker.onFocusField(field, value)"
@blur="field.tracker.onBlurField(field, value, errors.value)"
/>
</div>
</template>
<script>
export default {
extends: BaseField,
computed: {
booleanFromStoreConvertedToArray: {
get() {
const isSelected = this.value;
if (isSelected) {
return [this.field.model.itemId];
}
return [];
},
set(value) {
this.value = value.length > 0;
},
},
},
};
</script>
FormCheckbox.vue
<template>
<FormCheckboxBase
:checked="shouldBeChecked"
type="checkbox"
:class="$style.check"
:input-value="inputValue"
:title="title"
:id="id"
:name="name"
:required="required"
v-bind="$attrs"
@updateInput="updateInput"
/>
</template>
<script>
export default {
components: {
FormCheckboxBase,
},
props: {
value: {
type: Array,
default: () => [],
},
inputValue: {
// this is the value that will be assigned to the html element's value-property
type: String,
required: true,
},
title: {
type: String,
required: true,
},
id: {
type: String,
required: true,
},
name: {
type: String,
default: '',
},
required: {
type: Boolean,
default: false,
},
},
computed: {
shouldBeChecked() {
if (!this.value) {
return false;
}
return this.value.includes(this.inputValue);
},
},
methods: {
updateInput(event) {
const { checked, value } = event.target;
const newValue = [...this.value];
let checkLabel;
if (checked) {
checkLabel = 'on';
newValue.push(value);
} else {
checkLabel = 'off';
newValue.splice(newValue.indexOf(value), 1);
}
// this.$emit('input', newValue); <--- This is from Vue 2 which was working
},
},
};
</script>
FormCheckboxBase.vue
<template>
<div :class="$style['form-item']">
<input
:id="id"
:value="inputValue"
@change="updateInput"
:type="type"
v-bind="$attrs"
class="input"
:class="$style.input"
/>
<slot />
</div>
</template>
<script>
export default {
props: {
type: {
type: String,
default: 'checkbox',
},
inputValue: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
id: {
type: String,
required: true,
},
required: {
type: Boolean,
default: false,
},
checkboxList: {
type: Boolean,
default: false,
},
},
methods: {
updateInput(event) {
// this.$emit('updateInput', event); <--- This is From Vue2 Event Emitter
},
},
};
</script>
In order to address the issue of triggering the onInput
event twice and not updating the value when interacting with the field, I leveraged the BaseField component extension to ensure proper functionality.
BaseField.vue
onInput(value) {
this.value = value;
// validate
this.validate();
},