I can use an extractor (Callback<E, Observable[]> extractor
) to make a ListProperty
fire change events if one of its elements changed one of its properties (update event).
Update Change Event in ObservableList
Is there an equivalent for ObjectProperty<>
? I have an SimpleObjectProperty
which I want to fire events when properties of it's value (another bean type) change (update change events).
Sample code:
public class TestBean {
public static <T extends TestBean> Callback<T, Observable[]> extractor() {
return (final T o) -> new Observable[] { o.testPropertyProperty() };
}
private final StringProperty testProperty = new SimpleStringProperty();
public final StringProperty testPropertyProperty() {
return this.testProperty;
}
public final String getTestProperty() {
return this.testPropertyProperty().get();
}
public final void setTestProperty(final String testProperty) {
this.testPropertyProperty().set(testProperty);
}
}
public class SomeType {
/**
* How can I listen for changes of TestBean#testProperty?
*/
private final ObjectProperty<TestBean> property = new SimpleObjectProperty<>();
}
I want to receive change events if the value of SomeType#property
changes, but also, if SomeType#property#testProperty
changes.
I cannot just listen for SomeType#property#testProperty
, since I would not be notified when SomeType#property
was changed (I would then listen on the wrong object for changes).
I want to receive change events if value of
SomeType#property
changes, but also, ifSomeType#property#testProperty
changes.I cannot just listen for
SomeType#property#testProperty
, since I would not be notified, whenSomeType#property
was changed (I would then listen on the wrong object for changes).
This is a limitation of sorts of the current iteration of JavaFX. The built-in way is unreliable and you're better off using 3rd party libraries. See this answer for more information.
For you case, ReactFX can be utilized in a similar way:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.reactfx.value.Val;
import org.reactfx.value.Var;
class TestBean {
private final StringProperty testProperty = new SimpleStringProperty();
public final StringProperty testPropertyProperty() { return testProperty; }
public final String getTestProperty() { return testProperty.get(); }
public final void setTestProperty(String newTestProperty) { testProperty.set(newTestProperty); }
}
public class SomeType {
private final ObjectProperty<TestBean> property = new SimpleObjectProperty<>();
public final ObjectProperty<TestBean> propertyProperty() { return property; }
public final TestBean getProperty() { return property.get(); }
public final void setProperty(TestBean newProperty) { property.set(newProperty); }
public static void main(String[] args) {
SomeType someType = new SomeType();
Var<String> chainedTestProperty = Val.selectVar(someType.propertyProperty(), TestBean::testPropertyProperty);
chainedTestProperty.addListener((obs, oldVal, newVal) -> System.out.println(obs + " " + oldVal + "->" + newVal));
//Tests
someType.setProperty(new TestBean());
someType.getProperty().setTestProperty("s1");
TestBean bean2 = new TestBean();
bean2.setTestProperty("s2");
someType.setProperty(bean2);
someType.setProperty(new TestBean());
}
}
Output:
org.reactfx.value.FlatMappedVar@7aec35a null->s1
org.reactfx.value.FlatMappedVar@7aec35a s1->s2
org.reactfx.value.FlatMappedVar@7aec35a s2->null
The key line
Var<String> chainedTestProperty = Val.selectVar(someType.propertyProperty(), TestBean::testPropertyProperty);
is a sort of listener chaining. The first argument is a property (OvservableValue
) of some type Type
. The second argument is the "sub"-property of some other type Type2
inside Type
, which is given as a function from Type
to that property.
Now whenever any "links" in the chain change, you are notified. You can continue to listen to changes in sub-sub-... properties by continuously chaining ovservables this way.