Search code examples
javajavafxpresentation-model

JavaFX: Bindings with presentation model


My JavaFX application should look like this:

enter image description here

Now I want to make sure that the detail view adapts as soon as I select another person from the table view.

My classes so far:

public class Person {
    private final StringProperty name = new SimpleStringProperty();
    private final StringProperty title = new SimpleStringProperty();
    private final IntegerProperty age = new SimpleIntegerProperty();

    public Person(String name, String title, int age) {
        setName(name);
        setTitle(title);
        setAge(age);
    }
    // Getters and Setters
}

public class PresentationModel {

    private final ObservableList<Person> persons = FXCollections.observableArrayList();
    private final ObjectProperty<Person> selectedPerson = new SimpleObjectProperty<>();

    public PresentationModel() {
        // add some users to persons list
    }

  // Getters/Setters

}

In the UI class with the table I have set up a listener like this:

personsTable.getSelectionModel().selectedItemProperty().addListener((observable, oldPerson, newPerson) -> {
    model.setSelectedPerson(newPerson);
});

And in the UI class with the details view I have set up a binding:

nameLabel.textProperty().bind(model.getSelectedPerson().nameProperty());

The PresentationModel model attribute is created once with the start of the application and than passed through the constructors to all the UI classes.

But this bind is not working as expected. What can I change so the binding works correctly and the property changes?


Solution

  • The binding doesn't work, because getSelectedPerson just returns the current selected person, and isn't recomputed if the selected person changes.

    Using just the standard API, you can do

    nameLabel.textProperty().bind(Bindings.selectString(
        model.selectedPersonProperty(), "name"));
    

    This API is a little unsatisfactory in a number of ways. For one, there is no compile-time checking that the selectedPersonProperty() has a nameProperty(), and that it is of the correct type. Secondly, it uses reflection, which doesn't perform well in the case that you call it very frequently (which does not apply here). Finally, if the selected person is null, this will dump lots of superfluous warnings to standard output (despite the fact that the API documentation indicates this is a supported use case!!!).

    An alternative is provided by the ReactFX framework:

    nameLabel.textProperty().bind(Val.selectVar(
        model.selectedPersonProperty(), Person::nameProperty));