jwaliszko / ExpressiveAnnotations

Annotation-based conditional validation library.
MIT License
351 stars 123 forks source link

Lazy validation support needed (now, validation errors are shown immediately when dependent value is changed) #173

Open abw1023 opened 6 years ago

abw1023 commented 6 years ago

When I have a RequiredIf or AssertThat expression for field B that is dependent on field A, changing field A will immediately show the validation error on B, even though I have not yet attempted to submit the page. This violates the spirit of jquery.validate, which normally only shows validation errors once a submit is attempted on the page. I am requesting that expressive annotations be changed to follow the normal behavior of jquery.validate.

I have added the following work-around in validationHelper.validateReferences. There may be a better way to do this, but this works for my uses.

validator = $(form).validate(); // get validator attached to the form
// only validate if validator has been triggered
if (Object.keys(validator.submitted).length == 0) {
    logger.info(typeHelper.string.format('Validator has not been triggered, not validating dependencies of {0}.', name));
    return;
}
jwaliszko commented 6 years ago

This feature is here by design. If you'd like to mute dependent fields validation, switch it off:

<script>
    ea.settings.dependencyTriggers = '';

Please refer to the respective section of the documentation.

abw1023 commented 6 years ago

ea.settings.dependencyTriggers = '';

is not the same thing at all. This causes dependency validations to be turned off completely, except when submitted. What I am asking for is the default behavior of jquery.validate, which does not validate until the first submit is attempted, but then updates validations dynamically when values on the page change.

jwaliszko commented 6 years ago

OK, I think I understand the issue now - reopened.

I think the sentence "this violates the spirit of jquery.validate, which normally only shows validation errors once a submit is attempted on the page", is not true. It can be configured when a field should be validated, by setting the onkeyup, onfocusout or onclick options (see jquery.validate documentation). Normally, jquery.validate validates a field when it is unfocused - try to decorate some input field with e.g. standard StringLength attribute, provide a value and unselect it. Form doesn't need to be submitted earlier for the field to be validated.

When it comes to the EA logic responsible for dependent fields validation, such a fields are validated no matter if form is submitted or not - the dependencyTriggers events setting is solely responsible for that (a custom event can be defined and raised under specific custom circumstances). I prefer not to change the legacy behaviour of EA (at least not by default), not to surprise the other users. Nevertheless, if it is needed, we can think of introducing new configuration setting, e.g deferValidation - when true, form submission needs to be triggered first, for the fields validation to be unlocked.

I can do it unless you'd like to contribute with a pull request, which is always helpful. Thanks.

jwaliszko commented 6 years ago

One more note (just a noisy thinking). I've been looking at the jquery.validate documentation and noticed this sentence:

Before a field is marked as invalid, the validation is lazy: Before submitting the form for the first time, the user can tab through fields without getting annoying messages – they won't get bugged before having the chance to actually enter a correct value

This lazy validation is most likely something you have in mind when it comes to the dependencies validation. But the code Object.keys(validator.submitted).length == 0, which works for you, isn't probably quite right, is it? As far as I've debugged through it - the submitted object contains information of every invalid field, and is filled up systematically, when a particular field is validated, e.g. on unfocus prior to the form submission. So the form may be not submitted yet, while the submitted object contains some fields. It's unclear to me if this condition above is really what you wanted, but I may be wrong - I'm not that familiar with jquery.validate.

abw1023 commented 6 years ago

I've been digging through the code in jquery.validate and I agree with your statements. submitted is not the perfect solution, but I cannot find anything better. submitted[element.name] gets set any time an error is shown and it is never cleared except in a call to resetForm(). It is also used internally on the focusout, keyup and click events to flag whether a validation should be attempted.

So if there are any errors on the submit, then submitted will have keys and if there aren't any errors then the form will be submitted so it doesn't matter. If a field is programmatically validated and is invalid then submitted will also get a key, which is what makes it a less than perfect solution.

jwaliszko commented 6 years ago

After some consideration I do agree with you, that lazy validation is worth to be turned on by default. All in all user should be able to e.g. tab through the form fields, or should have a chance to provide correct field value, without triggering the immediate validation of dependent fields, and the same seeing the redundant error messages.

I've created separate lazy-valid branch to implement this feature. The initial proposal is pushed here: https://github.com/jwaliszko/ExpressiveAnnotations/commit/b0b86af92ef120b4df0fba15992232d89d10447b. You're more than welcome to review that in your spare time, and modify if noticed any issues.

When doing this changes I've been looking at the latest sources of jquery.validate (the script used in the EA web sample is outdated, but I still keep the old version there to be backward compatible).