Jones
Jones

Reputation: 578

How to update JavaFX binding reference of an Object

Consider the following models, Apple

public class Apple {
    private StringProperty appleName = new SimpleStringProperty("Apple");

    public String getAppleName() {
        return appleName.get();
    }

    public StringProperty appleNameProperty() {
        return appleName;
    }

    public void setAppleName(String appleName) {
        this.appleName.set(appleName);
    }
}

and Basket

public class Basket {
    private Apple apple = new Apple();

    public Apple getApple() {
        return apple;
    }

    public void setApple(Apple apple) {
        this.apple = apple;
    }
}

Basket has an apple. Now I'm trying to bind a simple string property as below.

public class Food{

    public static void main(String[] args) {
        StringProperty localApple = new SimpleStringProperty("lGreenApple");
        Basket basket = new Basket();
        Apple rGreenApple = new Apple();
        rGreenApple.setAppleName("rGreenApple");
        basket.setApple(rGreenApple);
        Bindings.bindBidirectional(localApple, rGreenApple.appleNameProperty());
        rGreenApple.appleNameProperty().set("rGreenApple 2");
        System.out.println(localApple.getValue()); //rGreenApple 2

        Apple redApple = new Apple();
        redApple.setAppleName("rRedApple");
        basket.setApple(redApple);
        redApple.appleNameProperty().set("rRedApple 2");
        System.out.println(localApple.getValue());//Still rGreenApple 2
    }
}

While trying to retrieve value after binding, still localApple object has reference to rGreenApple. What is the clean way to get the red apple?

Upvotes: 0

Views: 886

Answers (1)

James_D
James_D

Reputation: 209603

You need two things:

  1. Make the apple property in Basket a JavaFX observable property, instead of a regular JavaBean-style property.
  2. Bind the local string property to a "property of a property", i.e. the appleName property of the apple property of the Basket.

The first part is easy:

public class Basket {

    private final ObjectProperty<Apple> apple = new SimpleObjectProperty<>();

    public ObjectProperty<Apple> appleProperty() {
        return apple ;
    }

    public final Apple getApple() {
        return appleProperty().get();
    }

    public final void setApple(Apple apple) {
        appleProperty().set(apple);
    }
}

For the second part, the cleanest way is to use the EasyBind framework:

public class Food{

    public static void main(String[] args) {
        Basket basket = new Basket();
        Apple rGreenApple = new Apple();
        rGreenApple.setAppleName("rGreenApple");
        basket.setApple(rGreenApple);

        Property<String> localApple = EasyBind.monadic(basket.appleProperty())
            .selectProperty(Apple::appleNameProperty);

        rGreenApple.appleNameProperty().set("rGreenApple 2");
        System.out.println(localApple.getValue()); //rGreenApple 2

        Apple redApple = new Apple();
        redApple.setAppleName("rRedApple");
        basket.setApple(redApple);

        System.out.println(localApple.getValue());// rRedApple

        redApple.appleNameProperty().set("rRedApple 2");
        System.out.println(localApple.getValue());// rRedApple 2
    }
}

Without EasyBind, you have to manage the intermediate listeners by hand. Something like:

    StringProperty localApple = new SimpleStringProperty();
    localApple.bindBidirectional(basket.getApple().appleNameProperty());
    basket.appleProperty().addListener((obs, oldApple, newApple) -> {
        if (oldApple != null) {
            localApple.unbindBidirectional(oldApple.appleNameProperty());
        }
        if (newApple == null) {
            localApple.set("");
        } else {
            localApple.bindBidirectional(newApple.appleNameProperty());
        }
    });

Upvotes: 1

Related Questions