I am building a custom input component, using Vue 2.7 and the Composition API. So far, my componente has come to this state below:
<template>
<div class="ui-field">
<label :for="id">{{ label }}{{ required ? '*' : '' }}</label>
<input
:aria-errormessage="errorDescriptionId()"
:aria-invalid="!!error ? 'true' : 'false'"
:autocomplete="autocomplete"
class="ui-input"
:data-acting="is_acting"
:disabled="disabled"
:id="id"
:required="required"
:type="type"
:value="modelValue"
@input="emitChange"
/>
<p :id="errorDescriptionId()" v-if="error">{{ error }}</p>
</div>
</template>
<script lang="ts" setup>
type UIInput = {
autocomplete?: 'on' | 'off' | undefined
disabled?: boolean | undefined
error?: string | undefined
id?: string | undefined
is_acting?: boolean | undefined
label?: string | undefined
required?: boolean | undefined
type?: 'text' | 'number' | undefined
modelValue?: any
}
const props = defineProps<UIInput>()
const emit = defineEmits(['update:modelValue'])
function emitChange(event: Event): void {
if (event?.target?.value) {
emit('update:modelValue', event.target.value)
}
}
function errorDescriptionId(): string | undefined {
if (props.error === undefined) {
return undefined
}
return `${!!props.id ? props.id : generateRandomId()}-error`
}
function generateRandomId(): string {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
let random_string = ''
for (let i = 0; i < 4; i++) {
const index = Math.floor(Math.random() * letters.length)
random_string += letters.charAt(index)
}
return random_string
}
</script>
As you can see, the component should be able to receive a v-model
on the parent, like this:
<UIInput
autocomplete="off"
id="complement"
label="Complemento"
type="text"
v-model="form.address.complement"
/>
The form.address.complement
is a reactive object, and its value should be passed to my component's input element and changed as I change the input the text on the input. All of that is how the component should behave, but in reality, when checking Vue DevTools, my UIInput
has a modelValue: undefined
, even though form.address.complement
already starts with a string value, and also a $attrs: value: [value of form.address.complement]
. It's like the two-way binding isn't connecting everything properly.
I don't know what I am doing wrong, so if anyone can help, I'm thankful.
I found out how to work this out. The only things to change are the emit
, that needs to emit an event called "input"
emit('input', 'event.target.value')
and, in the type UIInput
, I dont need a modelValue
, as @Estus Flask commented on my question, but, actually, a value
, which serves as value to :value
in the input.