flow-components icon indicating copy to clipboard operation
flow-components copied to clipboard

[Grid] Selection not updated after DP refresh

Open stefanuebe opened this issue 1 year ago • 4 comments

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

  1. deactivate it explicitly (as it is now, so the dev has full control over it)
  2. refresh only selected items, when they are fetched
  3. 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

  1. Add the sample code to your Vaadin app
  2. The list should show items incl. iteration 0
  3. Select something. Click "Show". The notification should show the correct item.
  4. Click "Reset". The items in the grid are refreshed and show an increased iteration counter. The notification still shows the old item names.
  5. Click "Show" without changing the selection. The notification still shows the old item names.
  6. Select a different item and click "Show" again. The notification now shows the correct item name.
  7. Click "Switch Selection Mode". Repeat 3-6 to show the issue for multi select

Environment

Vaadin version(s): 24.3.10

Browsers

No response

stefanuebe avatar May 07 '24 06:05 stefanuebe

Dear @stefanuebe, did you try utilising the setSelectionPreservationMode? Would that be sufficient to cover your use-case?

yuriy-fix avatar May 16 '24 13:05 yuriy-fix

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.

stefanuebe avatar May 17 '24 07:05 stefanuebe

Related issue https://github.com/vaadin/flow-components/issues/3796

tomivirkki avatar Jun 06 '24 13:06 tomivirkki

Should this ticket be closed as the API is now in Vaadin 24.4 and newer?

TatuLund avatar Feb 26 '25 07:02 TatuLund

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.

web-padawan avatar Aug 27 '25 07:08 web-padawan