vuetify.jsvuetifyjs3

Vuetify validate a custom form component containing multiple internal form components


I have seen several posted questions with similar titles. For example, Vuetify apply rules validation to a custom component. However, they either (like that one) reference a situation where the rules can just be passed to a single internal form field component, or don't have an answer at all. To the communities defense, the questions that are closer to mine are typically poorly posed or unnecessarily complicated. So, I will try to pose a clear simple case that demonstrates my issue, in hopes that there is a simple answer.

Say I want to create a custom component <MyTextArea> that contains two fields, and I have validation rules that depend upon both of those fields. For example, I might want to be able to specify a rule that requires one and only one of the fields to be non-empty.

From what I have been able to find, I expect I would need to create a MyTextArea.vue file that looks something like

<script setup>
const model = defineModel()
const props = defineProps({
    rules: {
        type: Array,
        required: false
    }
}
</script>
<template>
<v-textarea label="field a" v-model="model.a"></v-textarea><br/>
<v-textarea label="field b" v-model="model.b"></v-textarea>
</template>

And where when I am using the component I want to be able to write

<v-form>
    ...
    <MyTextArea v-model="my_model" :rules="[value => !!value.a != !!value.b]" />
    ...
</v-form>

Since I can't just pass the specified rules on, I would expect that I need to implement a function in MyTextArea.vue that runs the rules, and returns the result, but I haven't found an example or any documentation on how to do this.

Any answers, including "hey bozo its right here at https:...", would be very welcome.

Thanks


Solution

  • Register the component and use internal validation from Vuetify through the useValidation() composable. The composable is not exposed in CDN version, so this only works in projects with a build chain.

    The reference usage can be found in implementation of VInput.

    Here is a simple example for your use-case:

    <template>
        <v-textarea
            label="field a"
            v-model="model.a"
            :readonly="isReadonly"
        ></v-textarea><br />
        <v-textarea
            label="field b"
            v-model="model.b"
            :readonly="isReadonly"
        ></v-textarea>
            
        <div class="text-error" v-if="!isValid">{{errorMessages.join()}}</div>
    </template>
    <script setup lang="ts">
    
    import { useValidation } from 'vuetify/lib/composables/validation.mjs'
    
    
    type ModelType = { a: string | null, b: string | null }
    const model = defineModel<ModelType>()
    const props = defineProps<{
        rules?: ((v: ModelType) => string | true)[]
    }>()
    
    const {
        errorMessages,
        isDirty,
        isDisabled,
        isReadonly,
        isPristine,
        isValid,
        isValidating,
        reset,
        resetValidation,
        validate,
        validationClasses
    } = useValidation({...props, modelValue: model}, 'v-input')
    
    </script>