I have a Vue 2.7 Composition API custom component that can have a v-model. The component looks like this:
<template>
<div class="ui-field">
<label :for="id">{{ label }}{{ required ? '*' : '' }}</label>
<the-mask
:aria-errormessage="errorDescriptionId()"
:aria-invalid="!!error ? 'true' : 'false'"
:autocomplete="autocomplete"
class="ui-input"
:data-acting="is_acting"
:disabled="disabled"
:id="id"
:mask="masks"
:masked="masked"
:required="required"
:type="type"
:v-model="model"
@input="emitChange"
/>
<i class="ui-field__locked fas fa-lock" v-if="disabled"></i>
<p :id="errorDescriptionId()" v-if="error">{{ error }}</p>
</div>
</template>
<script lang="ts" setup>
import { Ref, ref } from 'vue'
import type { UIMaskedInput } from './UIMaskedInput'
import useUiInput from '../UIInput/Core'
const props = withDefaults(defineProps<UIMaskedInput>(), { masked: false })
let model: Ref<any> = ref<any>(props.value)
const emit = defineEmits(['input'])
let { emitChange, errorDescriptionId } = useUiInput(props, emit)
</script>
Here you have its props and a composbale called "useUIInput"
export interface 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
value?: any
}
export interface UIMaskedInput extends UIInput {
masks: Array<string>
masked?: boolean
}
import { UIInput } from './UIInput'
export default function useUiInput(props: UIInput, emit) {
function emitChange(event: Event): void {
emit('input', '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
}
return { emitChange, errorDescriptionId }
}
It's a composable because it is used also by another custom input component, but the main difference from that one to this masked component is that the other is just an HTML input tag, while this has another component for input (a one that allows masking). For that simple input tag component, my custom v-model works properly, but for this one, I haven't yet found a way for it to work. If anyone has any ideas on how to, I am thankful if you can share.