Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make field components implement HasValidator interface #3406

Merged
merged 43 commits into from
Jul 11, 2022

Conversation

DiegoCardoso
Copy link
Contributor

@DiegoCardoso DiegoCardoso commented Jun 28, 2022

Description

  • Add HasValidator<T> to field components and make them override getDefaultValidator method
  • Make field components return string containing the reason for the validation error
  • Extract some common logic to an util class on the shared module

Fixes #3381

Type of change

  • Bugfix
  • Feature

Checklist

  • I have read the contribution guide: https://vaadin.com/docs-beta/latest/guide/contributing/overview/
  • I have added a description following the guideline.
  • The issue is created in the corresponding repository and I have referenced it.
  • I have added tests to ensure my change is effective and works as intended.
  • New and existing tests are passing locally with my change.
  • I have performed self-review and corrected misspellings.

Additional for Feature type of change

  • Enhancement / new feature was discussed in a corresponding GitHub issue and Acceptance Criteria were created.

@DiegoCardoso DiegoCardoso requested review from vursen and yuriy-fix June 28, 2022 11:46
import java.io.Serializable;

public final class ValidationError implements Serializable {
public static final String REQUIRED = "VALIDATION_REQUIRED_ERROR";
Copy link
Contributor

@knoobie knoobie Jun 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure those non-explaining error messages are not overriding errors provided by the binder (like asRequired("My awesome name of the field is empty").

Additionally, if those strings are ever shown to the end-user there should be a way to translate them. All fields should provide an i18n mapping for the possible errors and their outcome. Consider using an enum of possible errors with a translation key that can be look-up via getTranslation or other means.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure those non-explaining error messages are not overriding errors provided by the binder (like asRequired("My awesome name of the field is empty").

Hmmm. That's a valid point. Currently the HasValidator with Binder works so that the getDefaultValidator() is called before the validation constraints from Binder are called. So if asRequired() is called on the binding definition, that will effectively set the component's required property to true and the validation will fail with the VALIDATION_REQUIRED_ERROR message. From getDefaulltValidator(), I can't see a way of checking if there's a custom message defined on asRequired(), though.
I also don't think we can't remove the required check from the component (even when used with Binder), since my assumption is that it should work if the required is set on the component setRequired()/setRequiredIndicatorVisible()`. So I need to investigate how to proceed here.

Additionally, if those strings are ever shown to the end-user there should be a way to translate them. All fields should provide an i18n mapping for the possible errors and their outcome.

Good point. There should be a way of defining custom messages.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought: I don't think it's possible to fix this behavior on your side if getDefaultValidator runs before the binder validations. I think this should be fixed / done inside flow - and the getDefaultValidator() should run AFTER the binder validation; meaning the getDefaultValidator() should be registered once the field and it's validations are bind. This allows to stop the validations once the binder fails and uses this message instead of the messages / validations provided later by the field

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided to exclude required from the validation done by getDefaultValidator(), so if one wants to use binder, then only asRequired() should be used.

Copy link
Contributor

@knoobie knoobie Jun 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That raises multiple concerns.. short question: Can the field validation be disabled? We have moved all our validations to the binder over the years, because the client side wasn't secure and intuitive.

Just another example we currently use:

  • DatePicker:.setMax(LocalDate.now()); is used to disable future dates to get the user instant visible feedback that this date isn't selectable, but the user can still type tomorrow in.. resulting on a validation on the server with a proper error message like "Birthday can't be set to the future."

With this change, what error message is shown? The server side error message or the field validation? I would want the server side error message to be shown.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, thanks for all the feedback so far, they have been raising some internal conversation about the feature.

For now, we want to build a MVP to make this behaviour change on the fields to make client and flow constraints part of the binder validation. Later on, we will make the adjustments needed, like the ones you suggested.

With that said, we have been considering have an API to disable component validation and also to deliver these changes under a feature flag, so that we can release it and validate the new behaviour with more customers before making it the default behaviour.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deliver these changes under a feature flag

This would be really good! Thanks for the discussion!

Just to clarify: I'm totally with the addition of the feature, because I've requested this as "bug" 2 years ago (vaadin/flow#7767) - but it feels the currently MVP is behind the feature richness of the binder in terms of user and developer experience, that's why I wanted to mention it. Additionally because of the issue mentioned, we have migrated all our Vaadin 8 applications to the Binder only paradigm in V23.1 - where it would be a huge effort to adopt to a surprising change like this.

Something similar happened to us with an addtional Binder feature / behaviour change in the past, where it did something we didn't expect and had problems with the behavior change, because we heavily heavily really on Binder as the stable foundation of all our applications (validation and security) - vaadin/flow#10342

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion to overcome the current limitation of the hard-coded error messages:

Provide overloaded methods for the validation methods, like setPattern, setMax and so on with the new error message, default is used if no other message was provided.

Example:

var field = new TextField();
 // uses default error message
field.setPattern("^[a-zA-Z]$");
// uses default error message 
field.setPattern("^[a-zA-Z]$",  "Given input could not be validated. Pattern doesn't match."); 
// Function or something else that provides Locale 
field.setPattern("^[a-zA-Z]$",  locale -> getTranslation().translate("field.pattern.no-match", locale)); 
// BiFunction or something else that provides Field + Locale 
field.setPattern("^[a-zA-Z]$",  (field, locale) -> "Field" + field.getCaption + " is not valid.." ); 

@DiegoCardoso DiegoCardoso force-pushed the feat/implement-has-validator branch from f023328 to de36896 Compare July 4, 2022 14:17
@DiegoCardoso DiegoCardoso marked this pull request as ready for review July 4, 2022 14:35
Copy link
Contributor

@vursen vursen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of more suggestions to keep everything consistent.

DiegoCardoso and others added 4 commits July 8, 2022 14:37
…ow/src/test/java/com/vaadin/flow/component/datetimepicker/DateTimePickerBinderValidationTest.java

Co-authored-by: Sergey Vinogradov <[email protected]>
@vursen
Copy link
Contributor

vursen commented Jul 11, 2022

It would probably make sense to implement HasValidator also for RadioGroup and CheckboxGroup as they will need ValidationStatusChangeListener to trigger the binder validation on the validated event.

@DiegoCardoso DiegoCardoso removed the request for review from yuriy-fix July 11, 2022 09:21
@DiegoCardoso DiegoCardoso enabled auto-merge (squash) July 11, 2022 09:21
@sonarqubecloud
Copy link

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell C 132 Code Smells

No Coverage information No Coverage information
4.6% 4.6% Duplication

@DiegoCardoso DiegoCardoso merged commit ea71275 into master Jul 11, 2022
@DiegoCardoso DiegoCardoso deleted the feat/implement-has-validator branch July 11, 2022 09:52
taefi added a commit to vaadin/flow that referenced this pull request Jul 22, 2022
…on binder (#14205)

Add binder-level flag to allow disabling of
validation status change listener registration
for HasValidator fields.

Related to #13940 (comment) and vaadin/flow-components#3406 (comment)

(cherry picked from commit 420498c)

Co-authored-by: Knoobie <[email protected]>
vaadin-bot pushed a commit to vaadin/flow that referenced this pull request Jul 22, 2022
…on binder (#14205)

Add binder-level flag to allow disabling of
validation status change listener registration
for HasValidator fields.

Related to #13940 (comment) and vaadin/flow-components#3406 (comment)

(cherry picked from commit 420498c)

Co-authored-by: Knoobie <[email protected]>
mcollovati pushed a commit to vaadin/flow that referenced this pull request Jul 25, 2022
…on binder (#14205) (#14208)

Add binder-level flag to allow disabling of
validation status change listener registration
for HasValidator fields.

Related to #13940 (comment) and vaadin/flow-components#3406 (comment)

(cherry picked from commit 420498c)

Co-authored-by: Knoobie <[email protected]>
Co-authored-by: Soroosh Taefi <[email protected]>
@vaadin-bot
Copy link
Collaborator

This ticket/PR has been released with Vaadin 23.2.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement HasValidator interface
4 participants