I have a class called Passage
that contains a non-observable class called Action
. And Action
contains a SimpleStringProperty
.
class Passage {
Action action = null;
Action getAction() { return action; }
void setAction(Action action) { this.action = action; }
boolean hasAction() { return action != null; }
}
class Action {
StringProperty myStringProperty = new SimpleStringProperty();
StringProperty getStringProperty() { return myStringProperty; }
// and other methods, many of which modify myStringProperty
}
I would like to bind that string property to a UI Label. It seems like it should be a straightforward thing to do:
label.textProperty.bind(passage.getAction().getStringProperty());
However, Action
could sometimes be null. I've tried the following:
label.textProperty.bind(passage.hasAction() ? passage.getAction().getStringProperty() : new SimpleStringProperty("") );
Then, if Action starts out null (and therefore the label starts out blank) and then the Action is assigned to something non-null (i.e. setAction() is called), the label does not update to reflect the change.
Do I need to make Action observable? See: Binding to javafx properties of a object that can be null
The solution above uses monadic operations, which I'm not familiar with. So I read this helpful blog post: http://tomasmikula.github.io/blog/2014/03/26/monadic-operations-on-observablevalue.html
I have tried doing this binding using EasyBind.
I created a new class ObservableAction
that wraps an Action
, making it an observable value (though I'm not sure I did this step correctly):
import javafx.beans.binding.ObjectBinding;
public class ObservableAction extends ObjectBinding <Action>
{
private final Action value;
public ObservableAction(Action action) {
this.value = action;
}
@Override
public Action computeValue()
{
return value;
}
}
Then I have the following code in my ViewController (again, not sure I've done this correctly):
MonadicObservableValue<Action> monadicAction = EasyBind.monadic(new ObservableAction(passage.getAction()));
actionLabel.textProperty().bind(monadicAction.flatMap(action -> action.getTextProperty()).orElse(""));
The result is the same as I've experienced before: it works fine when the Action
is not null. However if the Action
starts null and then becomes non-null, the label is not updated.
I would recommend to have a property in Passage which will be binded with action's property. Once you set a new action you bind it's property and use only Passage's one.
class Passage {
private Action action;
private StringProperty actionMyStringProperty = new SimpleStringProperty();
void setAction(Action action) {
this.action = action;
// Unbind the previous binding if any (it is safe when nothing to unbind).
actionMyStringProperty.unbind();
if (action != null){
actionMyStringProperty.bind(action.getStringProperty());
}
}
public StringProperty actionMyStringProperty() {
return actionMyStringProperty;
}
}
class Action {
private StringProperty myStringProperty = new SimpleStringProperty();
StringProperty getStringProperty() {
return myStringProperty;
}
}
On client side:
label.textProperty.bind(passage.actionMyStringProperty());