Vaadin 24.4 integrates with React, unifies Flow and Hilla development, and more!
Blog

How to implement infinite scrolling using Vaadin components

By  
Matti Tahvonen
Matti Tahvonen
·
On Dec 21, 2022 2:40:09 PM
·

Infinite scrolling is a common UI pattern where more of a large data set is dynamically loaded from the server while users scroll through the page. The approach used by, for example, several social media platforms saves a lot of computing resources both on the client and server side without dramatically decreasing the user experience.

In Vaadin, Grid provides infinite scrolling out of the box with a pure Java API. Grid’s version of the UI pattern is very sophisticated as it can make the scrollbar behave without “surprises” at the end of the viewport if you can provide either row count or the full dataset on the server side. Another such component is VirtualList. But Grid also poses some limitations, like equal height rows. If you can’t or don’t want to use Grid, infinite scrolling can be built using other components, but we’ll need to get to a bit lower level in our abstractions.

In this article, I’ll show you how to implement infinite scrolling using some simple Vaadin core components. I’ll also show you the power of the Vaadin Flow framework: how to easily perform a bit of JS magic from the Java code.

Scroller - a component for scrollable content

To begin, we'll need a scrollable container and enough content to display a scrollbar. The Scroller component (a successor to the Panel component in older Vaadin versions) provides scrollbars for our “infinitely long” content. As content, we'll use VerticalLayout and stuff it with paragraphs to provide the scroller component with a scrollbar.

A tiny gotcha with Scroller is that it doesn't have a defined height by default, so it grows based on its content. Based on your use case, you should either use a fixed height or the full height of the available space, as we are doing in the example below. In the example, we expose the component to the root context with the Route annotation.

@Route("")
public class InfiniteScrollerView extends Scroller {
    VerticalLayout content = new VerticalLayout();
    public InfiniteScrollerView() {
        setHeightFull();
        for (int i = 0; i < 100; i++) {
            content.add(new Paragraph("Row" + i));
        }
        setContent(content);
    }
}

Add a method to add more rows

In a real-world infinite scroll implementation, when you reach the end, you will most likely do a backend query and, based on the results, add some additional components to the existing layout. To simulate this, let’s add a method called loadMoreRows that simply adds more paragraphs to the layout.

In the next step, we will call this Java method from a JS listener executed in the browser. Thus, let’s also annotate this method with @ClientCallable annotation. This exposes a “client side proxy” to this method into the element of our scroller component.

    @ClientCallable
    public void loadMoreRows() {
        int componentCount = content.getComponentCount();
        for (int i = componentCount; i < componentCount + 100; i++) {
            content.add(new Paragraph("new item, row id " + i));
        }
    }

Add a client side-scrolling listener to track scrolling to the end

The current Scroller component in Vaadin is quite basic It technically just uses a tiny CSS rule to provide the scrollable area. Thus, to listen for scrolling events, we need to get to some lower-level browser development.

All Java components in Vaadin Flow are backed up by a DOM element on the client-side. By calling getElement(), we can get a “server-side proxy” to this browser element, and using its executeJS method. We can call arbitrary JS code on the element. The this keyword in that JS refers to the element on which the method is called, so it is also a useful route to call the server side method we exposed to the browser side in the previous step.

By adding the following Java code snippet to our constructor, we start to listen to all scroll events of our Scroller component and call the server-side method (only) when we have reached the end of the content.

        getElement().executeJs("""
        var el = this;
        el.addEventListener("scroll", function(e) {
            if(el.scrollTop + el.clientHeight == el.scrollHeight) {
                el.$server.loadMoreRows();
            }
        });
        """);

If you are wondering about the triple quotation marks, that’s the new multi-line String syntax available since Java 17 for the general audience. Newer Java versions make some Vaadin code look much better.

Notes and considerations for actual usage

If the capabilities of the Vaadin Flow framework are misused, they can have a negative effect on the quality of your code.  It's very easy to break the abstraction in the manner indicated in this method, resulting in application code that resembles a tangled mess. Despite the fact that this example is a "small JS hack,", my suggestion is to hide all these kinds of tricks in a superclass or in a generic helper library (that you can share via Directory). This way, your actual UI logic remains pure maintainable Java code.

To make your life a little bit easier regarding scrolling, I implemented a VScroller extension to Viritin add-on that you can use to implement similar logic with pure Java. It also provides a method to get all (meaningful) scroll events and to scroll the container to an arbitrary position. There is also a related WindowScroller class suitable for scrolling the actual browser window instead.

Check out the full example via GitHub -->

Matti Tahvonen
Matti Tahvonen
Matti Tahvonen has a long history in Vaadin R&D: developing the core framework from the dark ages of pure JS client side to the GWT era and creating number of official and unofficial Vaadin add-ons. His current responsibility is to keep you up to date with latest and greatest Vaadin related technologies. You can follow him on Twitter – @MattiTahvonen
Other posts by Matti Tahvonen