Validator and Status on Field Binder

We have bumped into unexpected behavior from the BindingValidationStatus.
The following fragment is in the constructor of a Composite<Dialog>:

binder.forField(tagComboBox)
                .withValidator((tags, context) -> {
                    System.out.println("Binder has changes: " + binder.hasChanges());
                    if (isValid(tags) && binder.hasChanges()) {
                        System.out.println("Tags are valid");
                        return ValidationResult.ok();
                    } else {
                        System.out.println("Tags are NOT valid");
                        var error = ValidationResult.error("Not yet supported");
                        System.out.println(error);
                        return error;
                    }
                })
                .withValidationStatusHandler(
                        status -> {
                            status.getValidationResults().stream().forEach(System.out::println);
                            System.out.println(status.getValidationResults().isEmpty());
                            System.out.println(status.isError());
//                            System.out.println("Validation status isOk: " + isOk);
//                            applyButton.setEnabled(isOk);

                        })
                .bind(x -> x, (x, y) -> {
                    selectedTags.clear();
                    selectedTags.addAll(y);
                });

While debugging with the print statements, we noticed that when an error is expected in the list of ValidationResults. There is nothing:

true
false
Binder has changes: false
Tags are NOT valid
ValidationResult{error='Not yet supported', errorLevel=ERROR}
true
false

While we were expecting:

true
true
Binder has changes: false
Tags are NOT valid
ValidationResult{error='Not yet supported', errorLevel=ERROR}
{error}
false
true

This is playing with our validation and how our apply button behaves. As it gets an OK instead of an error.

Is there anyone who recognizes this issue and/or knows a good solution to this?

It is not possible to say anything to this without transcript of the user actions and what else is happening with the form, assuming ComboBox is not the only field there.
But as a brief comment I can say that your approach of using Binding#withValidationStatusHandler for just toggling button enabled state is probably not the right one. Note, adding Binder#withValidationStatusHandler will disable the default error label / indicator behaviors. So if you want the ComboBox be invalid state and show the error message, you should do that in the status handler. Binding#withValidationStatusHandler is mainly purposed to override these default behaviors.
So instead you should use Binder#addStatusChangeListener

Hi Tatu Lund

Thank you for your response.
In this case, the ComboBox is the only field in the form. It has an Apply button that saves Tags to specific entity types. And it has a cancel button.
That’s it. So there is not much other interaction involved.

We don’t want to show error labels in this case. The behaviour we are setting here is that, as long as no change has taken place, the apply button should not be accessable.
I tried with the status change listener, but it has the same issue.

Although we return the ValidationResult.error(...) through the conditional statement. Later on the status Handler / Status Change Listener is acting asif that error never took place and says there are no errors. That is unexpected. It does pick-up OKs, but not the errors.

The funny thing is that the actual functionality works. But when we look closer and look at the status of it all, it doesn’t make sense that it’s working. And our Unit tests are failing on this.

Then I see Binder here as un-necessary complication, and I would just use ComboBox as is and value change listener in it. Binder sure can be used in single field forms, but typically it does not add much value.

I would also say, that hasChanges inside validator may not work as expected, as it depends a bit on Binders internal logic when that changes flag inside Binder is being updated.