Hi Alban,
Thanks for your reply. I also did some investigation and I copied the classes in my project, so I can find a solution easier and create an issue in the gitHub repo. The problem comes from the ListBoxBase, because there are attached some listeners to the dataProvider and the items are refreshed on DataRefreshEvent, but the filtering triggers a DataChangeEvent, which rebuilds the component and clears the selection.
private void rebuild() {
clear();
removeAll();
...
}
I created a new method, which refreshes all items, doesn’t clear the selection and I use the default one only when the component is initialized.
I thought about reporting this, but then I faced the second problem - the selection is based on index. That means that if you select something and filter, the same positions will be selected after that, but you will already have different items.
I found a solution for that as well by changing the presentationToModel and modelToPresentation methods in the MultiSelectListBox:
private static <T> Set<T> presentationToModel(final MultiSelectListBox<T> listBox, final JsonArray presentation) {
// If the data provider is filtered and an item is selected, but not shown, it has an index -1, so we have to exclude it
final Set<T> selectedValues = IntStream.range(0, presentation.length())
.map(i -> (int) presentation.getNumber(i))
.filter(i -> i >= 0)
.mapToObj(index -> listBox.getItems()
.get(index))
.collect(Collectors.toSet());
// We search for values which are selected, but not in the data provider in case of filtering
final Set<T> valuesToAdd = listBox.getValue()
.stream()
.filter(value -> !listBox.getItems()
.contains(value))
.collect(Collectors.toSet());
selectedValues.addAll(valuesToAdd);
return Collections.unmodifiableSet(selectedValues);
}
private static <T> JsonArray modelToPresentation(final MultiSelectListBox<T> listBox, final Set<T> model) {
final JsonArray array = Json.createArray();
model.stream()
.map(listBox.getItems()::indexOf)
.forEach(index -> array.set(array.length(), index));
return array;
}
I also call setPresentationValue(getValue()) in the new refresh method to update the frontend after filtering.
However… this works, but I gave up reporting this. The component is not meant to be used for my case. The last problem (which is unsolvable on my side and I read, that the component is “designed” to work like this and from Vaadin don’t plan to change it) was that all the items are rendered eagerly in the frontend. With 100 items it’s a bit slow, when I have 1000 items it takes around 2-3 seconds to show them, with 10000 the application is unsuable, So I decided to throw away that in the garbage and use a grid with a single column. I styled it to look the same and it works perfect.
If your interested in my solution, or have any questions, feel free to ask.