I am writing a JavaFx control that consists of a sub-control that gets user input, e.g. a TextField. The main component holds a property which represents a parsed representation of the text, e.g. a LocalDateTime. When the user enters something, this property should be updated, so I bind it to the child's value property. It should also be possible to change the current value from outside via binding the value property, so this has to be a bidirectional binding to update the child automatically. The control works fine until client code binds to the property. The following code shows my problem:
import javafx.beans.property.SimpleIntegerProperty;
public class Playbook {
// The internal control with a property p
static class Child {
public SimpleIntegerProperty p = new SimpleIntegerProperty();
public void set(int i) {p.set(i);}
}
// My new control with a property value which is bound
// bidirectionally to Child.p
static class Parent {
public Child a1;
public SimpleIntegerProperty value = new SimpleIntegerProperty();
public Parent() {
a1 = new Child();
value.bindBidirectional(a1.p);
}
}
public static void main(String[] args) {
Parent p = new Parent();
// some client code wants to keep the
// value updated and thus binds to it
SimpleIntegerProperty outside = new SimpleIntegerProperty();
p.value.bind(outside);
// simulate a change in the child control
p.a1.p.set(10);
}
}
When I run the code, I get an exception that a bound property cannot be set:
Caused by: java.lang.RuntimeException: A bound value cannot be set.
at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:143)
at com.sun.javafx.binding.BidirectionalBinding$BidirectionalIntegerBinding.changed(BidirectionalBinding.java:467)
I am sure this must be a common problem and I am just not seeing the obvious solution. I am using ReactFx, so any solution with plain JavaFx or ReactFx would be welcome. The real code uses a Var.mapBidirectional to bind the Parent and Child properties internally.
What I want to achieve is the following:
1. If outside's value changes, this should be propagated to p.value and then to p.a1.p
2. If p.a1.p changes, this should be propagated to p.value
From this I concluded that Parent.value and Parent.a1.p are always identical (plus some tranformation applied in the mapping) and this I use a bidrectional mapping. outside can change independently and can be different to value, so I use a unidirectional binding.
After understanding that JavaFx Bindings are not what I thought it would be and that the semantics do not allow this, I switched to ReactFx's
EventStream/Val/Var. ReactFx's model seems to be a better match to my expectation which allows me to create a system that behaves as follows:The following code implements this. It uses two
Vars which are bound bidrectionally and thus changes in one of them are fed to the otherVar. Changes from outside are defined as anEventStreamwhich is fed to one of theVars.The program runs and prints