Best practice for updating validation on form fields

I’m looking for best practice with regard to form validation and updating as the form is filled in. The use case is fairly common (essentially showing that the user has fixed the error as they type as opposed to making them resubmit to find out):

  1. User fills in form
  2. Turbo sends form to server
  3. 422 response with validation errors in the html
    For each error:
  4. Customer fixes error in input
  5. Validation error html disappears
  6. Customer resubmits

For asp.net mvc (in the .Net framework days), they implemented this both server and client side. i.e. they duplicated the validation code and the JS code prevented normal form submission. As this was part of the framework, it worked quite well in simple cases (think it relied upon jquery validate library) but whenever your validation wasn’t “out of the box” you had to implement it twice yourself.

With turbo I can imagine several solutions:

  1. On input change, send a request to the backend for validation (similar to the form submission), use turbo to update the html (with errors presumably now gone). With Turbo 8, I presume this would actually be quite nice due to the dom morphing? Before 8, you could maybe use turbo frames or turbo streams to update only the validation parts of the page? This pattern feels “turbo” and enables you to write your validation logic once.

  2. On input change, use client side stimulus controller to update validation errors. But then this leads to duplication of code and logic across BE/FE which doesn’t feel very turbo.

Given how common this scenario is in web dev, I can’t believe it’s not covered in the handbook or the rails docs (but I just can’t seem to find an authoritative answer or an answer at all)

Thanks

Are you suggesting that on each field change, you validate just that field in isolation, server-side, somehow? Or could you simply reset the validation warning after any change, and then let the next form submit take care of the next validation? I’ve done the latter, just by clearing the style with some simple and generic code, basically (awful made-up pseudocode):

.field_with_errors input onChange
  remove field_with_errors className from parent

That gets you away from the icky UX of having correct input in an error-flavored field.

The first option seems like it could get quite complex, particularly if your model has any compound validations (only validate this field if that other one is valued…).

Walter

It doesn’t have to be that field in isolation.

With the new dom morphing it’s quite conceivable that you could send the entire form to a validate endpoint that does the same validation, returns the form and then morphs those dom changes. This would be v easy for developers to set and maintain (you could probably do this easily by convention). Obviously, the downside is that you’re now making loads of server requests so you debounce, only do on blur, handle simple validation client side etc etc

Given the rails doctrine of optimising for developer happiness this sort of solution seems aligned. Wondering what others do/what is recommended (if anything is).

In our applications, we use standard browser validation attributes for client-side. We add a little JS to make the user experience a better

  1. Run initial field validation on blur (client side)
  2. Re-validate field on-change
  3. Stop form submit if any fields are invalid and jump to that field
  4. Backend validations (e.g. duplicate record already exists) run on the backend and page is re-rendered displaying the backend errors

The majority of our validations can be handled with standard HTML validations (required, email format, etc). The trick was using javascript to capture the client browser error, and then displaying the error in a more uniform (better styled) error format.

Note: the bonus is this validation works with (or without) JS enabled.

Thanks for the reply. That does sound like you get to a pretty good UX without adding loads of JS.

Doesn’t sound particularly turbo specific either. Are you able to share the code or do you know of any useful libraries?

We built our own solution. I’m not aware of any libraries that do this specifically, but there are multiple articles out there that show different ways to do this.

For example:

With a spot of googling, the closest I came to something that seemed along your lines was this library: