Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange Behaviour with EasyBind.map #2

Closed
ckeimel opened this issue Oct 9, 2014 · 2 comments
Closed

Strange Behaviour with EasyBind.map #2

ckeimel opened this issue Oct 9, 2014 · 2 comments

Comments

@ckeimel
Copy link

ckeimel commented Oct 9, 2014

Hello

Thanks a lot for sharing this code. It's been realy helpful in lot of situations!

I am experiencing some strange behaviour with 'EasyBind.map'. My goal is to derive a list of conditions (Strings) from a collection of buisiness objects, that will be passed to the application server on a search request.

Basically, what I am doing is this:

ObservableList<MyDataObject> selectedItems = checkComboBox.getCheckModel().getCheckedItems();
ObservableList<String> conditions = EasyBind.map(selectedItems, p -> p.getCondition());
sendRequestToServer(conditions);

What I am seeing is that 'conditions' is always emtpy because 'conditions' will not be updated although the 'selectedItems' change.

The following code has a dependency on ControlsFX (8.20.7). If you run it like this and check the first entry in the combo box, only the 'ListChangeListener' on 'selectedItems' will fire, but not the one on the 'MappedList'.

Now please uncomment the two lines 74 and 75:

ListView<String> selectedView = new ListView<String>(lastNames);
box.getChildren().add(selectedView);

and run the program again. Now, if an item is checked, the selection will be displayed in the new list and both 'ListChangeListener' instances are called!

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;

import org.controlsfx.control.CheckComboBox;
import org.fxmisc.easybind.EasyBind;

public class MapTest extends Application {
    public static void main(String[] args) { 
        launch(args); 
    } 

    private ObservableList<MapTestPerson> people = FXCollections.observableArrayList();

    @Override
    public void start(Stage primaryStage) throws Exception {
        initPeople();
        primaryStage.setScene(new Scene(createContent(primaryStage), 420, 200));
        primaryStage.show(); 
    }

    private void initPeople() {
        people.add(new MapTestPerson("Alfonso", "Cuarón"));
        people.add(new MapTestPerson("Ang", "Lee"));
        people.add(new MapTestPerson("Michel", "Hazanavicius"));
        people.add(new MapTestPerson("Tom", "Hooper"));
    }

    private Parent createContent(Stage primaryStage) {
        BorderPane pane = new BorderPane();
        pane.setPadding(new Insets(12));
        VBox box = new VBox(6);
        pane.setCenter(box);

        CheckComboBox<MapTestPerson> view = new CheckComboBox<MapTestPerson>(people);
        view.setConverter(new StringConverter<MapTestPerson>() {
            @Override
            public String toString(MapTestPerson object) {
                return object.getFirstName() + " " + object.getLastName();
            }
            @Override
            public MapTestPerson fromString(String string) {
                return null;
            }
        });
        box.getChildren().add(view);

        final ObservableList<MapTestPerson> selectedItems = view.getCheckModel().getCheckedItems();
        selectedItems.addListener((ListChangeListener<MapTestPerson>)change -> {
            while (change.next()) {
                System.out.println("selectedItems change: " + change);
            }
        });

        ObservableList<String> lastNames = EasyBind.map(selectedItems, p -> p.getLastName());
        lastNames.addListener((ListChangeListener<String>)change -> {
            while (change.next()) {
                System.out.println("lastNames change: " + change);
            }
        });

//      ListView<String> selectedView = new ListView<String>(lastNames);
//      box.getChildren().add(selectedView);

        return pane;
    }
}

class MapTestPerson {
    private StringProperty firstName = new SimpleStringProperty();
    private StringProperty lastName = new SimpleStringProperty();

    public MapTestPerson(String first, String last) {
        firstName.set(first);
        lastName.set(last);
    }

    @Override
    public String toString() {
        return firstName + " " + lastName;
    }

    public final StringProperty firstNameProperty() {
        return this.firstName;
    }

    public final String getFirstName() {
        return this.firstNameProperty().get();
    }

    public final void setFirstName(final String firstName) {
        this.firstNameProperty().set(firstName);
    }

    public final StringProperty lastNameProperty() {
        return this.lastName;
    }

    public final String getLastName() {
        return this.lastNameProperty().get();
    }

    public final void setLastName(final String lastName) {
        this.lastNameProperty().set(lastName);
    }
}
@TomasMikula
Copy link
Owner

Hi,

I haven't run your code, but it looks like lastNames is garbage collected (together with the attached listener). This is the trouble with weak listeners---selectedItems only holds a weak reference to the mapped list. You get the same problem with bindings from JavaFX, e.g. widthProperty().add(10.0).addListener(/* may never execute */).

Make sure you maintain a reference to the mapped list and let me know if that solved your problem.

Best,
Tomas

@ckeimel
Copy link
Author

ckeimel commented Oct 10, 2014

Hi Tomas

thanks for the quick reply! You are absolutely correct. If I declare lastNames as a field variable it works as expected.

Smile
Christoph

@ckeimel ckeimel closed this as completed Oct 10, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants