0

The following code gives me trouble when I click on one of the generated checkboxes:

public class Controller implements Initializable {
    ObservableList<String> strings = FXCollections.observableArrayList("a", "b", "c");

    @FXML
    public HBox x;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Bindings.bindContent(x.getChildren(), EasyBind.map(strings, s -> {
            CheckBox c = new CheckBox(s);
            c.setOnAction(event -> {
                strings.remove(c.getText());
                System.out.println("removed");
            });
            return c;
        }));
    }
}

What I'm aiming at is having an observable list of strings, and that when that list is changed, checkboxes in the HBox get created / removed accordingly.

The error I'm getting is:

Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Children: duplicate children added: parent = HBox[id=x, styleClass=root]
    at javafx.scene.Parent$2.onProposedChange(Parent.java:454)
    at com.sun.javafx.collections.VetoableListDecorator$VetoableSubListDecorator.clear(VetoableListDecorator.java:529)
    at com.sun.javafx.binding.ContentBinding$ListContentBinding.onChanged(ContentBinding.java:114)
    at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
    at org.fxmisc.easybind.MappedList.sourceChanged(MappedList.java:37)
    at javafx.collections.transformation.TransformationList.lambda$getListener$23(TransformationList.java:106)
    at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
    at com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
    at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
    at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
    at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
    at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:183)
    at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:171)
    at sample.Controller.lambda$null$0(Controller.java:28)

It seems that JavaFX / EasyBind have trouble removing the element, but it's not entirely clear why?

EDIT:

Added some print statement when creating the checkboxes:

Creating checkbox a
Creating checkbox b
Creating checkbox c
Creating checkbox a
Creating checkbox b
Creating checkbox c

Something is triggering twice the creation of checkboxes, but it's still not entirely clear to me what.

Thanks

Miss Chanandler Bong
  • 4,081
  • 10
  • 26
  • 36
devoured elysium
  • 101,373
  • 131
  • 340
  • 557
  • 1
    Not been able to identify the exact source of the issue, but returning the same objects from the lambda seems to fix it: `EasyBind.map(strings, s -> boxes.computeIfAbsent(s, text -> { CheckBox cb = new CheckBox(text); cb.setOnAction(event -> { strings.remove(text); boxes.remove(text); System.out.println("removed"); }); return cb; }))` – fabian Jan 23 '19 at 10:19
  • @fabian Yes, it seems like a workaround. Is EasyBind a library that is widely used in production systems or more of a garage-type thing? I'm trying to assess if it's worth it to try to make it work or alternatively I'll just try my luck with more mundane approaches. – devoured elysium Jan 23 '19 at 10:44
  • It turned clear what the issue is. The problem is not EasyBind, it seems (I still have to verify it) but that when comparing items, it uses the target types (in this case, Checkboxes) instead of the source types to make the "diff". As the checkboxes just use their references as the basis for equality, the checks all fail. Defining their equals/hashcode based on the getText() fixes all the problems. This doesn't make any sense at all. – devoured elysium Jan 23 '19 at 12:22
  • I would expect for the bindings to keep track of the mapping between the observed and observer objects and to always use the observed objects to do any kind of diff logic.. – devoured elysium Jan 23 '19 at 12:23

0 Answers0