Skip to content

Conversation

@tltv
Copy link
Member

@tltv tltv commented Jan 28, 2026

Adds Binder.Binding.value() to be used in field binding validators to read value of another field. The Binder automatically runs validators inside a ComponentEffect#effect context, making validators and converters reactive to signal changes.

Fixes: #23298

@tltv tltv added the signals label Jan 28, 2026
tltv added 3 commits January 29, 2026 10:09
Adds Binder.Binding.value() to be used in field binding validators to read value of another field. The Binder automatically runs validators inside a ComponentEffect#effect context, making validators and converters reactive to signal changes.

Part of #23298
@tltv tltv force-pushed the feat/binder-signal-validation branch from a0fa6ef to 2569dd3 Compare January 29, 2026 08:13
@github-actions
Copy link

github-actions bot commented Jan 29, 2026

Test Results

1 340 files  + 1  1 340 suites  +1   1h 16m 29s ⏱️ +12s
9 474 tests +24  9 406 ✅ +24  68 💤 ±0  0 ❌ ±0 
9 942 runs  +24  9 866 ✅ +24  76 💤 ±0  0 ❌ ±0 

Results for commit 488224d. ± Comparison against base commit 2f5bba4.

♻️ This comment has been updated with latest results.

Bean level validators throws Binder.InvalidSignalUsageError error when Signal.value() is called in them.

Fixes: #23364
@mshabarov mshabarov self-requested a review January 30, 2026 08:57

private transient Registration signalRegistration;

private boolean initialSignalRegistration = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems to be well aligned with the existing valueInit flag. Would it make sense to reuse that one?

if (signalRegistration == null
&& getField() instanceof Component component) {
initialSignalRegistration = true;
signalRegistration = ComponentEffect.effect(component, () -> {
Copy link
Contributor

Choose a reason for hiding this comment

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

As I understand, this makes validators and converters signal-reactive for validation purposes. Given that I use a signal in my converter function, what happens regarding value updates arising from a signal-triggered change?

After reading the code, seems that such updates won't make their way to fields. If so, is it intentional? Would it make sense to run converters outside of effect context?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's intentionally running also converters inside the effect. But in practice I don't believe signals are actually used in converters. If they are, then validation reruns also on those signal value changes.
I didn't see earlier nice technical solution to leave converters out of scope here. But now I see one, it's to wrap each converter execution inside UsageTracker.untracked(...). Do you think this would make sense?

* "Both fields must match")
* .bind("password");
*
* UI.getCurrent().add(passwordField, confirmField);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd avoid suggesting UI.getCurrent() in the javadoc, would it be enough to just keep add(passwordField, confirmField); ?

if (signalRegistration != null) {
signalRegistration.remove();
signalRegistration = null;
internalValidationTriggerSignal = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need initialSignalRegistration=true as well?

* // error
*
* // or same with a Signal:
* confirmSignal.value("secret"); // passwordField shows validation
Copy link
Contributor

Choose a reason for hiding this comment

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

Javadoc could also show the initialization of confirmSignal and how it is bound to the confirm password field.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually confirmSignal is not used in the validator at all.

Perhaps it's better to add a validator to the confirm password field instead of password field.

All in all, I'd suggest to document exactly the codes from the ticket. It uses both types of dependency - binding value and signal value.

/**
* Error thrown when a signal is used incorrectly.
*/
public static class InvalidSignalUsageError extends Error {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd suggest extending IllegalStateException as Error is something that indicates serious problems.

throw new InvalidSignalUsageError(
"Detected Signal.value() call inside a bean level validator. "
+ "This is not supported since bean level validators "
+ "are not run inside a reactive effect.");
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd add a sentence "Using Signal.value() is only supported in field level validators."

Copy link
Contributor

@mshabarov mshabarov left a comment

Choose a reason for hiding this comment

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

Few minor comments, but looks good to me and also works as expected with an example from the ticket.

* feat: add binder validation status signal

Adds Binder.getValidationStatus() method to get read-only signal for BinderValidationStatus.

Fixes: #23300

* fix single binding validation status change

* fix unresolved validation status change
@github-actions github-actions bot added +1.0.0 and removed +0.1.0 labels Feb 3, 2026
@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 3, 2026

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.

Binder: Cross-Field validation

4 participants