JavaFX binding and null values


Question

I was wondering how to bind values where the source of the bind could be null.

I have a property:

private ObjectProperty<Operation> operation = new SimpleObjectProperty<>(null);

I also have a text field:

@FXML
private Text txtCurrentOperation;

I would like to bind the textProperty of the field to the value of the operation object.

My first thought was to use FluentAPI with its when/then/otherwise construct, but it is eagerly evaluated so the solution:

Bindings.when(operation.isNotNull())
    .then("null")
    .otherwise(operation.get().getName()));

will throw a NPE, because the parameter of otherwise is evaluated no matter what the result of the when.

My next idea was to use lambda somehow:

txtCurrentOperation.textProperty().bind(() ->
        new SimpleStringProperty(
             operation.isNotNull().get() ? "Null" : operation.get().getName()
            ));

But the bind has no lambda enabled solution. (Later I realized that it couldn't have, becasue the real work goes backward: the change of the binded object (operation) will trigger the update of the binder (the field text property).)

Some articles I found suggested to use an "extremal" value for the property instead of null. But Operation is a complex and heavy weight component so it is not trivial to construct an artifical instance to represent null. Even more, this seems to me boilercode, something the binding mechanism is designed to help eliminating.

My next try was to logically swap the binding direction and add listener to the operation property and let it update the field programatically. It works and rather simple as long as the need of update only depends the operation object instances:

 operation.addListener((e) -> {
        txtCurrentOperation.setText(operation.isNull().get() ? 
            "Null" : operation.get().getName());
 });
 operation.set(oper);

It is relatively simple, but doesn't work: it throws "A bound value cannot be set." exception and I don't see why is the text property of the control regarded as bound.

I ran out of ideas. After much searching, I still cannot solve the simple problem to update a text field differently based on whether the source is null or not.

This seems so simple and everyday problem, that I am sure I missed the solution.

1
9
9/19/2014 3:20:08 PM

Accepted Answer

If a 3rd party library is an option, check out EasyBind. Try something like this:

EasyBind.select(operation)
        .selectObject(Operation::nameProperty)
        .orElse("null");

There's also a JavaFX JIRA issue for the type of functionality provided by EasyBind. If you don't want to use a 3rd party library, try Bindings.select:

Bindings.when(operation.isNotNull())
    .then("null")
    .otherwise(Bindings.select(operation, "name"));

Be aware the null checking in Bindings.select isn't super efficient. There's a JIRA issue for it.

6
2/6/2016 1:41:46 PM

Just in case if somebody using not Java itself but Kotlin. It is a good idea to use wonderful tornadofx library. There you can just use operation.select{it.name}. Although, this feature seems not to be documented yet, so it took some time to discover it.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon