Fixing Vuetify’s Form Validation

Robert Mirabelle
4 min readAug 31, 2020

--

Quite unfortunately, Vuetify form validation effectively arrives “broken” out of the box. Here, I’ll explain the problem and suggest a solution.

The Problem

ALL Vuetify form inputs are immediately/eagerly validated (on blur) — and they shouldn’t be.

The worldwide industry standard for form validation (for at least the past decade or so) is to wait to eagerly validate inputs until submit is attempted at least once.

By immediately eagerly validating every input in a form, Vuetify introduces some ugly (if not downright buggy) behaviors. Chief among these is the inability to cancel editing of a form once any of its inputs has been so much as focused.

For example, one convenience I add to most of my forms is auto-focusing of the first input:

<v-form>
<v-text-field autofocus :rules="[v => !!v || 'Required']"/>
<v-btn>Cancel</v-btn>
</v-form>

Having completed no fields, click on the cancel button or ANYWHERE outside the form, and behold in terror as Vuetify first flags the entire form as invalid (by rendering error messages on every required input), then registers the click.

The reason this occurs is because clicking on any other control or link on the page causes blur() to be fired on the currently focused input, which in turn automatically triggers Vuetify’s over-eager form validation, despite the fact that no attempt to enter any data has occurred.

Support for cancellation is one of the primary reasons the standard for validation is to wait to eagerly validate inputs until after submit is attempted.

The Unfix

There are a couple of Vuetify form options that seem like they might help, but both are dead ends:

form.lazy-validation

From the Vuetify form API docs:

If enabled, value will always be true unless there are visible validation errors. You can still call validate() to manually trigger validation.

Here, value is used to represent whether the form is currently valid. Ignoring the fact that value is a horrid and misleading name (having nothing whatsoever to do with validity), setting lazy-validation to true appears to do nothing at all. The form happily ignores this and is still eagerly validated!

v-text-field.validate-on-blur

Also from the Vuetify API docs:

Delays validation until blur event

Initially, we might intuit that we could prevent eager input validation by setting :validate-on-blur=”false on each text field, but doing so reverses the intended usage of this property. AND it doesn’t work.

The Fix

To restore form validation to the expected standard (wait for submit, then eagerly validate), we’ll need to jump through some hoops. Luckily, the solution is pretty straightforward, if a bit long-winded.

Start by assigning a ref to the form so we can later validate it manually:

<v-form ref=”form” />

Declare a rules property, but leave its value empty for now:

data() {
return {
rules: {}
}
}

You can then assign the empty rules to inputs:

<v-text-field :rules=”rules.firstName” />

Vuetify will still eagerly validate this field on blur, but the rules object is currently empty, so no rules will be violated.

Create a submit button and/or wire up form submission based on <Enter>:

<v-form ref="form">
<v-text-field :rules=”rules.firstName” @keyup.enter="onSubmit"/>
<v-btn @click="onSubmit">Submit</v-btn>
</v-form>

Once the the form’s submit button is clicked, manually trigger validation by first hot-updating rules to use actual rules. Then, wait for the DOM to be updated. Then call the Vuetify validate() method:

onSubmit() {
//replace empty rules with real rules
this.rules = {
firstName: [v => !!v || 'Required']
}
//let the DOM update
this.$nextTick(() => {
//manually trigger Vuetify validation
if(this.$refs.form.validate()) {
//it’s valid, do work
}
//if not valid, errors will be automatically displayed
})
}

If hosting the form in a v-dialog, the dialog should have the eager
attribute assigned, so its HTML is immediately added to the DOM so
we can access the form and reset it when the dialog is shown or hidden:

watch: {
shown() { //when dialog visibility changed
this.rules = {} //clear rules
this.$refs.form.reset() //reset the form (requires ‘eager’)
}
}

This validation fix requires thinking about form validity a little
differently. Instead of following the recommended approach of setting
v-model=”valid” on the form, we want to enable the submit button not
based on validity, but rather the form being ‘dirty’.

All Together Now

Here’s a complete example. In this example, we render a form inside a dialog. The form isn’t validated until the submit button is clicked. After attempting to submit, then we allow eager validation as expected. When the dialog is shown or hidden, we reset both the form and rules, so we can start over when the dialog is re-shown.

If the Vuetify team doesn’t want to update v-form to adhere to validation best practices, they could address this issue alternatively by creating an additional v-form prop that allows us to bypass immediate/eager validation until the form has been submitted. Naturally, we’d expect lazy-validation to do the trick, but as stated above, setting this prop appears to do nothing. It’s unclear to me whether I’ve uncovered a bug or am just misunderstanding the intended usage.

Hope this helps!

--

--