[Grid] Selection not updated after DP refresh
Description
This is a follow up of #1265. The issue is the same as in the described ticket.
When calling one of the refresh methods of a Grid's DataProvider, the selection inside the grid still refers to the old objects.
Expected outcome
The grid's selected items should be updated, when the data provider is refreshed, so that calling "getSelectedItems" refers to the same objects, that were returned from the DataProvider.
Lazy loading and multi select performance This featue can lead to performance issues, when using lazy loading and multi select, for instance when selecting a huge amount of items. To give devs the possibility to react on this problem, it should be possible on multi select mode to
- deactivate it explicitly (as it is now, so the dev has full control over it)
- refresh only selected items, when they are fetched
- refresh explicitly all selected items
for instance via the selection model, the grid or the data provider (I would say the grid selection model should be the most fitting one?). Something like
Multi#setUpdateSelectedItemsOnRefreshMode()
enum UpdateSelectedItemsOnRefreshMode {
NEVER,
ONLY_FETCHED,
ALL
}
From my side there is no request, which should be the default. I think the only fetched is maybe the most fitting for most use cases.
Case 2 means, that, when for instance all items are selected, that when showing the first 50 items, only those 50 items are updated and the remaining ones are still outdated. When triggering the next fetch by scrolling down, the next 50 items, that are fetched also will update the respective items in the grid's selection.
Minimal reproducible example
@Route("")
public class GridOutdatedSelectionIssueView extends VerticalLayout {
private final Grid<Data> grid;
private List<Data> BACKEND;
private int iteration;
public GridOutdatedSelectionIssueView() {
initData();
grid = new Grid<>(Data.class);
grid.setItems(DataProvider.fromCallbacks(
query -> BACKEND.stream()
.skip(query.getOffset())
.limit(query.getLimit()),
query -> BACKEND.size()
));
Button reset = new Button("Reset", event -> {
initData(); // simulate data has changed in the backend
grid.getDataProvider().refreshAll();
showSelection();
});
Button show = new Button("Show", event -> showSelection());
Button mode = new Button("Switch Selection Mode", event -> {
if (grid.getSelectionModel() instanceof SelectionModel.Single<?, ?>) {
grid.setSelectionMode(Grid.SelectionMode.MULTI);
} else {
grid.setSelectionMode(Grid.SelectionMode.SINGLE);
}
});
add(new HorizontalLayout(reset, show, mode), grid);
setFlexGrow(1, grid);
setHorizontalComponentAlignment(Alignment.STRETCH, grid);
}
private void showSelection() {
Set<Data> selectedItems = grid.getSelectedItems();
String collect = selectedItems.stream().map(Data::getName).collect(Collectors.joining("; "));
Notification.show(!collect.isEmpty() ? collect : "No items selected");
}
public void initData() {
BACKEND = LongStream.range(0, 200)
.mapToObj(id -> new Data(id, "Item " + id + " (iteration " + iteration + ")"))
.toList();
iteration++;
}
@RequiredArgsConstructor
@Getter
@EqualsAndHashCode(of = "id")
public static class Data {
private final long id;
private final String name;
}
}
Steps to reproduce
- Add the sample code to your Vaadin app
- The list should show items incl. iteration 0
- Select something. Click "Show". The notification should show the correct item.
- Click "Reset". The items in the grid are refreshed and show an increased iteration counter. The notification still shows the old item names.
- Click "Show" without changing the selection. The notification still shows the old item names.
- Select a different item and click "Show" again. The notification now shows the correct item name.
- Click "Switch Selection Mode". Repeat 3-6 to show the issue for multi select
Environment
Vaadin version(s): 24.3.10
Browsers
No response
Dear @stefanuebe, did you try utilising the setSelectionPreservationMode? Would that be sufficient to cover your use-case?
No, unfortunately not. The selection is still pointing to the old items. Also since we use a lazy DP, the existing variant could not be tested.
Related issue https://github.com/vaadin/flow-components/issues/3796
Should this ticket be closed as the API is now in Vaadin 24.4 and newer?
From https://github.com/vaadin/flow-components/issues/7898#issuecomment-3224303876:
The preservation mode affects the selection itself, but the problem I described is that the actual objects in the selection are outdated. The values displayed in the grid are updated, but when you call getSelectedItems, you still get the old objects which hold the old values.