November 09, 2020
Some time ago, I wrote about the need for simple HTML5 form validation in ~6 lines of JavaScript. Well, I’m back on my bullshit, this time in a Vue 2/Nuxt application. I finally hit the point on this project where I needed some form validation. Rather than jumping headlong into a whole form builder library dependency, I thought I’d try my simple method again. Here’s a demo of it in action.
See the Pen Happier HTML5 Form Validation in Vue by Dave Rupert (@davatron5000) on CodePen.
There are a few differences compared to the vanilla DOM implementation. The non-framework version relied on querySelectorAll
and addEventListener
to loop through inputs, which I guess you could do inside a Vue component, but feels a bit like an anti-pattern, so I had to modify the approach a smidge.
First step is I’m applying an .errors
styling class to the parent <form>
when one of the inputs invalidates. This is triggered by the component-level this.errors
state. Originally the class was put on the input
, but we can safely put it on the form instead of each individual input’s state.
The second bit is the @invalid
bind directive on each required
input. This replaces looping through each input
and adding an addEventListener
but has the same effect of attaching a method to the invalid
event. The invalidateForm()
method helps us visually invalidate the form and just like before HTML does all the submit
event cancellation for us.
We initialize the form component with errors: false
because we don’t want the error styling until the user has submitted the form. The invalidateForm
function just sets this.error = true
. That’s one problem with the CSS :invalid
pseudo class, it’s way too eager. Hooking into the invalid
events, delays the styling until after the first form submission attempt and we know the form has errors.
Now, when the form
has errors, then we can apply the :invalid
styles. I put this in as a global style but you do you.
Aggressive form validation situations like on blur
or on change
might change the utility of this approach and you’d probably need some kind of input-level state management. I’m trying to avoid a component-per-input situation, so I’m hoping I don’t run into this and even if I had that concern I could maybe layer it into this approach.
Overall, I’m pleased again with this lightweight solution. It’s a bit repetitive across form pages in my Nuxt app, but I’m able to yank it out if we need a library for more robust error handling. Maybe I can find a way to abstract this, because that is something I do miss from the querySelectorAll
approach, I just had to write it once, or hang it off of selector, but in a Vue context, I’m doing this n
times for every page with a form rather than just once. I irrationally loathe wrapper components, but maybe there’s a way to make this less repetitive.
Home • About • Archive • Likes • Bookshelf • RSS
© 2020 Dave Rupert • Twitter • Github