aspnet-client-validation icon indicating copy to clipboard operation
aspnet-client-validation copied to clipboard

Do not validate immediately

Open lonix1 opened this issue 1 year ago • 9 comments

In the original jQuery plugin it was possible to control when validation was activated. By default it would occur on each field's blur (demo).

This library performs validation immediately, i.e. as soon as the user starts typing in a field. That is not standard validation behaviour and leads to poor UX - the user is distracted by validation errors even before submitting the form.

Repro: I wanted to ensure it's not "just me", so:

  • I cloned the repo and ran the sample app
  • In the "Form 1" sample, type anything into the "Id (42)" field
  • Even before submitting, the validation errors are shown

I can identify these modes of activation:

  • current behaviour: validation is activated (for each field) immediately as the user types into a field
  • on form submit: validation is activated (for all fields) only when the user attempts to submit the form
  • on field blur (original plugin's behaviour): validation is activated (for each field) only after a field's first blur
  • on field blur with change: validation is activated (for each field) only after a field's blur and where the value was actually changed (to protect against user simply tabbing/clicking through fields without actually changing them)

Is any of this currently possible?

lonix1 avatar Mar 02 '24 00:03 lonix1

By default it would occur on each field's blur

I believe the intent is still to be as close as we can to a drop-in replacement, so aligning our default with the old library seems reasonable.

I can't think of any reason we wouldn't always want to capture fields' initial value so we can defer validation until that value has changed. We'd still always validate on submit, of course.

Is any of this currently possible

You can specify data-val-event="blur" per field to override: https://github.com/haacked/aspnet-client-validation/blob/2c76d3567ea60f695f6f7d043620d8d2e6c8548b/src/index.ts#L1042-L1049

We can take a closer look at how the old library can be customized, but in the short term we could pretty easily support bootstrapping with { fieldValidationEvent: 'blur' } override for all fields.

Thoughts?

dahlbyk avatar Mar 04 '24 05:03 dahlbyk

Thanks!

capture fields' initial value so we can defer validation until that value has changed

Yes I did something similar - I saved the initial value to a data attribute, e.g. data-original="foo" for each field, on document ready.

You can specify data-val-event="blur" per field to override

I was able to delete so many kludges and workarounds because of that feature. Thanks! I've lodged a docs PR to expose it.

bootstrapping with { fieldValidationEvent: 'blur' } override for all fields

Although that would be nice, but data-val-event="blur" is probably good enough for now.

But deferring validating until the field is actually dirty would be a good addition. Without that, some ugly workarounds are still necessary.

I haven't written TypeScript in years, so I can't help there, but maybe someone will stumble on this issue for the same reason and help out. Just ping me to get involved and help with testing / documenting, etc.

lonix1 avatar Mar 05 '24 11:03 lonix1

Was this addressed in #110?

haacked avatar May 06 '24 20:05 haacked

Was this addressed in #110?

I don't think so

dahlbyk avatar May 06 '24 21:05 dahlbyk

Would like to add my two cents - ideally the logic would be a little bit more complicated than switching validation event to blur. I think it shouldn't do any validation while the user typing for the first time (i.e. until the first blur or submit) but once the validation had failed and user focused back into the input, we should switch back to "input" event to remove the error as soon as the data is valid.

upd: I was playing with the code there a little bit trying to implement the desired behavior and noticed that data-val-event value isn't really used - looks like parenthesis are missing in this line: https://github.com/haacked/aspnet-client-validation/blob/b8b996585a7cc20d2fb31174e6410d3b101db0f3/src/index.ts#L1092 So whenever there's a data-val-event attribute present, no matter it's value, "change" event will be used. Not sure if it is intentional though.

andkorsh avatar Aug 08 '24 21:08 andkorsh

The original plugin's behaviour is to validate on blur, so that should be the default given that this library is intended to be a drop-in replacement.

That said, I suspect we're describing the same thing?

lonix1 avatar Aug 09 '24 02:08 lonix1

@lonix1 I've created a PR that adds the new behavior for "delayed validation" using a new option like this:

   new ValidationService().bootstrap({ delayedValidation: true })

The default behavior is unchanged and will continue to be a drop-in replacement.

Apart of that, I found a bug with data-val-event that I have described above, which I have not fixed in my PR on purpose, because it changes the way data-val-event works. I can easily fix it, but it will change the behavior for people using this attribute. As it was suggested in the comments above, some may have specified data-val-event="blur", but due to a bug, when this attribute specified, it in fact uses "change" event, not "blur".

Although delayedValidation option might be the most desirable, let me know if I should fix the bug with data-val-event and I can do that in the same PR.

andkorsh avatar Aug 09 '24 14:08 andkorsh

I didn't realise you put it behind a new option, in which case it's all good. I suppose Keith / Phil should decide on the rest.

At some point we should also discuss the default behaviour as documented above.

lonix1 avatar Aug 10 '24 03:08 lonix1

I'm increasingly finding the original default (validate on input/change or blur) is aggravating, and that many (/most?) modern sites don't tell you a field is invalid until you actually try to submit the form. Much better UX.

So I disagree with my original point above... the default is still necessary to entice users to switch to this library (which is how it works now, thanks to contribution by @andkorsh), but some day it would be nice to also have an option to defer validation for the entire form until the first submit attempt.

My workaround, for now:

If a form should be validated only on submit, add an attribute data-val-event="submit". Then add this into your scripts:

let v = new aspnetValidation.ValidationService();
v.bootstrap();

document.querySelectorAll('form[data-val-event="submit"]')
  .forEach(form => {
    v.remove(form);
    form.addEventListener('submit', handleSubmit);
    function handleSubmit(ev) {
      form.removeEventListener('submit', handleSubmit);
      v.scan(form);
      if (!v.isValid(form)) ev.preventDefault();
    }
  });

As you can see, it bootstraps as normal, then removes the form - horrible, but I found no other way. Then it listens for the first submit event, and re-adds validation.

A limitation: due to isValid, this doesn't work for async validators. (It can be easily modified to use a callback instead.)

Ideally, simply adding that attribute to the form should be the solution, someday. For now this works (for me).

lonix1 avatar Feb 11 '25 04:02 lonix1