vaadin / flow-components

Java counterpart of Vaadin Web Components
101 stars 66 forks source link

Grid#scrollToIndex does not work in afterNavigation when called after Grid#setItems #6711

Open TatuLund opened 1 month ago

TatuLund commented 1 month ago

Description

If I set both the items in the afterNavigation and then immediately scroll to index, Grid is not scrolling. There is apparently some glitch with timing. Possibly Grid rows are not rendered yet when scroll is attempted. This is very likely scenario as Grid data is often fetched from backend and set in afterNavigation, furthermore target item can be url parameter.

Expected outcome

Grid should have been scrolled to target index.

Minimal reproducible example

@Route(value = "grid-scroll", layout = MainLayout.class)
public class GridScrollIssue extends VerticalLayout
        implements AfterNavigationObserver {

    Grid<String> grid;

    public GridScrollIssue() {
        grid = new Grid<>();
        grid.addColumn(item -> item).setHeader("Item");
        var button = new Button("Scroll", e -> grid.scrollToIndex(200));
        add(grid, button);
    }

    @Override
    public void afterNavigation(AfterNavigationEvent event) {
        grid.setItems(IntStream.range(0, 1000).mapToObj(i -> "Item " + i)
                .collect(Collectors.toList()));
        grid.scrollToIndex(200);
    }
}

Steps to reproduce

Navigate to view and observe that Grid is not scrolled. Clicking the button will scroll the Grid to right row.

Environment

Vaadin version(s): 24.4.12

Browsers

No response

TatuLund commented 1 month ago

The workaround is to scroll to index after a delay.

@Route(value = "grid-scroll", layout = MainLayout.class)
public class GridScrollIssue extends VerticalLayout
        implements AfterNavigationObserver {

    Grid<String> grid;

    public GridScrollIssue() {
        grid = new Grid<>();
        grid.addColumn(item -> item).setHeader("Item");
        var button = new Button("Scroll", e -> scrollAfterDelay());
        add(grid, button);
    }

    @Override
    public void afterNavigation(AfterNavigationEvent event) {
        grid.setItems(IntStream.range(0, 1000).mapToObj(i -> "Item " + i)
                .collect(Collectors.toList()));
        getElement().executeJs(
                "setTimeout(() => $0.$server.scrollAfterDelay(), 200)",
                getElement());
    }

    @ClientCallable
    public void scrollAfterDelay() {
        grid.scrollToIndex(200);
    }
}
TatuLund commented 1 month ago

May be related: https://vaadin.com/forum/t/grid-and-preserving-selection/166814/4