MultiSelectListBox filter does not work correctly

I have a TextField that also implements Predicate
public class FilterStringTextField<T> extends TextField implements SerializablePredicate<T>

so it can be used in
MultiSelectListBox.getListDataView().setFilter(...)

Everytime something is typed in the textfield, it calls setFilter. The implementation of predicate may vary, but usually is “if the textfield is empty return true, else do a case insensitive contains”. This initially seems to work perfectly. So first all are shown:

Then the filter is set, a subset of rows are shown, and the 3rd row is selected.

And then the filter textfield is cleared, which makes the predicate always return true so all are shown again:

But the 3rd row is selected! Which is the wrong element. It keeps the presention index, not the model index, selected when changing the filter.

Vaadin 24.7.5.

Have you implemented hashCode() and equals()? I haven’t tried MultiSelectListBox, but other Vaadin components such as grid and select won’t work correctly if those methods aren’t producing a correct result.

Yes I have. But if equals and hashcode were the cause, then the behavior would not be as predictable as it is now: it always keeps the presentation model index selected, not the data model index.

Could be a bug, but difficult to say without seeing some code. You could open an issue with a reproduction here: GitHub · Where software is built

Will do, but this code shows the problem:


@Route("/test")
public class TestView extends VerticalLayout {

    public TestView() {
        TextField textField = new TextField();
        textField.setClearButtonVisible(true);
        textField.setValueChangeMode(ValueChangeMode.EAGER);
        add(textField);

        MultiSelectListBox<String> listBox = new MultiSelectListBox<>();
        listBox.setItems("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten");
        listBox.setSelectionPreservationMode(SelectionPreservationMode.PRESERVE_EXISTING);
        add(listBox);

        ListBoxListDataView<String> listDataView = listBox.getListDataView();
        textField.addValueChangeListener(e -> listDataView.setFilter((SerializablePredicate<String>) s -> {
            String filterContent = textField.getValue().toLowerCase();
            return filterContent.isEmpty() || s.toLowerCase().contains(filterContent);
        }));
    }
}