Close

Lazy loading with Vaadin 8

One of my favorite new features in Vaadin 8 is the Grid::setDataProvider method which makes it remarkably easy to implement lazy loading in Grids. In earlier versions of Vaadin, you had to implement a rather complex Container interface. Vaadin 8 not only removes this interface, but also provides a modern API that takes advantage of many Java 8 features.

In this blog post, you will learn how to implement lazy loading to show a sortable list of people in a Grid component by simply providing two lambda expressions. You can find two “flavors” of the example application: One for people who use Spring (Spring Boot), and one for people who use Java EE (CDI and WildFly Swarm):

What is lazy loading?

Lazy loading is a technique used to delay the loading of resources to the point where it’s actually needed. Say you have a database table with 1000 rows, and you want to show this data in a UI with space for 20 rows at a time. You can query just the first 20 rows and when the user scrolls down the list, then you query the next 20 rows, and so forth. If you fetch all the data from your data source, your app will consume more memory and will take longer to show something on the screen.

When you want to display tons of data, you would probably use Vaadin’s Grid. By default, Grid performs lazy loading between the client and the server, which already improves performance a lot. This means that you have lazy loading out-of-the-box between the Grid component (in the browser) and the web server. How you query your data source is entirely up to you. If you try to fetch all the data at once, then the Grid will only send what’s needed when it’s needed to the client. However, all the data will stay in the memory on the server-side. Fortunately, the Grid class provides the mechanisms to allow you to query your data source in a lazy way. Let’s see how to do it!

The domain model

Suppose you have a domain class like the following:

public class Person {

    private Long id;
    private String firstName;
    private String lastName;
    private String email;

    ... getter and setters ...
}

And a service class like this:

public class PersonService {

    public List<Person> findAll(int offset, int limit) {
        ...
    }

    public int count() {
        ...
    }
}
 

The actual implementation of the methods depends on your specific persistence technologies. You can find a full implementation of the Person and PersonService classes for both Spring and Java EE applications using the links provided in the introduction.

Lazy loading functionality with Grid

You obviously need an instance of the PersonService class and a new Grid:

PersonService service = new PersonService();
Grid<Person> grid = new Grid<>(Person.class);

Notice how, in Vaadin 8, Grid is parameterized with the domain type (Person). Now, instead of using the infamous grid.setItems(service.findAll()), you can use the setDataProvider method and pass:

  1. A lambda expression to return a “slice” of the data and,

  2. Another lambda expression to return the total count of people in the data source.

You can delegate these operations to the service class:

grid.setDataProvider(
    (sortOrders, offset, limit) ->
            service.findAll(offset, limit).stream(),
    () -> service.count()
);

Ordering functionality

If you read the previous snippet of code, you might have noticed the sortOrders parameter in the first lambda expression. sortOrder is a List of QuerySortOrder objects that you can use to tell your service how to order the data.

The PersonService::findAll method accepts a Map with String keys representing the name of a Java property in the Person class, and a Boolean value telling whether to sort the property in ascending order. So, for this example, you have to translate between a List<QuerySortOrder> and a Map<String, Boolean>). Some Java should do the job:

Map<String, Boolean> sortOrder = new LinkedHashMap<>();

for (QuerySortOrder order : sortOrders) {
    sortOrder.put(order.getSorted(),
            SortDirection.ASCENDING.equals(order.getDirection()));
}

As you can see, the QuerySortOrder::getSorted method returns a string with the name of the Java property, and the SortOrder::getDirection method (QuerySortOrder extends SortOrder) returns a value from the SortDirection enum.

And that’s it! You can pass this map to the service, so the complete call to the setDataProvider method would look like this:

grid.setDataProvider(
    (sortOrders, offset, limit) -> {
        Map<String, Boolean> sortOrder = sortOrders.stream()
                .collect(Collectors.toMap(
                        sort -> sort.getSorted(),
                        sort -> SortDirection.ASCENDING.equals(
                                sort.getDirection())));

        return service.findAll(offset, limit, sortOrder).stream();
    },
    () -> service.count()
);

Learn more about Vaadin 8

Comparing Polymer and Angular from a developer’s perspective

In my previous post, I showed that Polymer has a significant performance advantage over Angular. If you are trying to make your app fast (you should be), you are probably wondering if it would make sense to build it with Polymer.

The potential problem with building an app with Polymer is that Polymer was not initially designed for building applications. It’s a library to help developers work with Web Component APIs (think jQuery for the Web Components APIs). 

Fortunately, the component-based development model offered by Web Components is flexible enough that you can build complex applications using the browser as your framework. But while it’s possible to create complex apps with Polymer, it’s still missing the structure and guidance a framework gives you.

In this post, I will compare the development experiences in building the same application in both Angular and Polymer.

Tools and languages

Both Angular and Polymer come with command-line (CLI) tools that help developers set up and build projects. The CLI tools hide much of the complexity of the build process: taking care of things like transpilation, optimizing and bundling code for production. The Angular CLI additionally (and optionally) performs an Ahead-of-Time (AOT) compilation step to reduce the amount of JavaScript processing that is needed in the browser. 

Polymer

Since Polymer is based on Web standards, components are built with HTML and JavaScript. In Polymer 2 and later, the default language level is ES6, which will get transpiled down to ES5 by the CLI to support older browsers. 

A simple Polymer component.

Polymer is still fairly limited when it comes to IDE support. The Polymer team is working on improving that through the Polymer IDE plugin that can be used with editors like VS Code and Atom. The Polymer IDE plugin helps developers avoid common mistakes, and offers some autocomplete help. Because Polymer uses plain ES6, there are no type checks.

Angular

Angular supports development in TypeScript, JavaScript, and Dart. Most documentation and examples that are available online are using TypeScript, which is the de-facto language for building Angular apps. TypeScript is a superset of JavaScript that adds type checking. Type checking gives developers an added layer of checks on the code and can be helpful to ensure APIs are used correctly, especially when working in bigger teams or when using third party libraries. 

A simple Angular component. 

The use of TypeScript in Angular also makes it easier for IDEs to offer development time help and checks. Visual Studio Code and WebStorm both have good support for TypeScript, the latter even helping the developer with including imports, making Angular development in an IDE fast despite the amount of boilerplate.

Debugging

Debugging Polymer applications is straightforward. You can use the browser developer tools to set breakpoints and inspect the running code just like you would with any other front-end technology. 

Most often, debugging the Angular app boiled down to a series of console.log statements

Angular is much more complex to debug due to the build steps between your code and the code that is running in the browser. The Angular CLI build creates source maps that should allow you to debug the TypeScript code in the browser. In practice, this didn’t work well as the source maps didn’t match the running code and breakpoints didn’t work. There is also a Chrome plugin for debugging Angular apps called Augury that allows you to inspect the hierarchy and status of your components. I did not find the plugin to be very useful in pinpointing issues. Most often, debugging the Angular app boiled down to a series of console.log statements. Error messages in the browser console were cryptic, and stack traces rarely include identifiable code, only Angular internal calls. 

Application structure and routing

Since both Angular and Polymer are component-based, the general application structure is similar. The UI is constructed out of components that get combined into successively more complex components, which are nested until an app emerges. In Angular, you can additionally bundle related components into modules that are used to scope dependencies, and that can be loaded lazily when needed.

Larger applications are organized into views. The views are typically bound to URLs so that users can link to different parts of the application and use browser navigation buttons to move back and forth. This mapping between view components and URLs is handled by a router. 

Angular ships with a somewhat complex but feature-rich router that supports arbitrarily deeply nested routes. It helps the developer keep the application’s JavaScript size manageable by lazily loading code for modules that aren’t needed until later. It further allows routes to be equipped with Guards, code that is run before or after a view gets activated, to conditionally prevent navigation. The router is configured in a TypeScript file through nested objects.

Polymer ships an optional router that can be used to map between URLs and components. Using the router requires several steps and a lot of repetitive code. You need to add an app-location and anapp-route element to your app to listen for URL changes. Using this information, you can decide what components to show, and optionally (manually) hook up lazy loading of those components. 

By default, Polymer components are only lazily loaded into the DOM, but not removed. This leads to the DOM growing over time in large applications which may result in performance issues, especially on devices with limited memory. 

The lack of a good router is the single biggest factor making it difficult to build large applications in Polymer

The Polymer router also supports nested routes. In practice, however, using nested routes gets very complicated as the router doesn’t know which view is active so sub-routes will get triggered on all views regardless if they are visible. The router does not either hook up to the build tools, so you need to separately configure views for lazy loading. 

Based on my experience, I feel that the lack of a good router is the single biggest factor making it difficult to build large applications in Polymer. 

Working with data

Data binding and data flow are important aspects of building apps. One of the functionalities that Polymer provides on top of the Web Components standards is a simple data binding system that supports both one-way and two-way binding of data. One-way data binding is preferred over two-way binding to make it easier to integrate the components with other frameworks and to enable uni-directional data flow patterns like Redux.

Angular also comes with support for both one-way and two-way data binding. In addition to data binding, it also comes with a Dependency Injection (DI) system that makes it easier to hook up services, especially when testing. Angular builds heavily on RxJS and Observables for handling data, especially for asynchronous HTTP communication. The reactive programming model will take some getting used to if you don’t have experience from before, but enables you to build elegant UI code. 

Editing data in forms is somewhat different in Polymer and Angular. Polymer has a form element that extends the HTML form. All validation is handled by the form elements themselves, which is a bit limiting at times. In Angular, the validation is handled by the framework which makes it easier to combine different components but still have consistent validation.

Making it look nice

When it comes to customizing the look and feel of your app, Angular is much easier to work with than Polymer. Angular works together with existing CSS libraries like Bootstrap or Semantic UI and you can easily write global CSS to give your app a cohesive look. 

Polymer is harder to customize in terms of look and feel because Web Components are designed to encapsulate their implementations, which means that you need to declare CSS variables and mixins that you import in your components. This means that you cannot use something like Boostrap easily and that it’s hard to get third party components fit your applications look and feel. 

Angular also makes working with dynamic styles easier than polymer, as the template syntax includes a helper for setting style names based on a condition. In Polymer, you need to rely on basic data binding and custom methods which adds a lot of boilerplate.

Mobile support: Progressive Web Apps, native apps, or both?

Angular and Polymer have different approaches when it comes to mobile support. Angular is more focused on today’s needs, so it includes support for building both Web applications and native applications (although native support requires you to write a separate implementation of the view). They also added support for creating Progressive Web Applications (PWA) recently. 

For Polymer, PWA is the only mobile strategy. This is in line with Polymer’s goal of building on Web standards to expand what you can do on the Web. 

Maintainability and stability 

Angular and Polymer both have a less than stellar track record when it comes to API stability. Polymer had a very rough transition period leading from version 0.5 until 1.0. Likewise, Angular 2 went through an extended period of API changes during development, lasting throughout the release candidates. There have even been some smaller breaking changes within the “stable” 2.x version. 

The good news is that both teams have taken note and are planning for smoother upgrade paths in the future. Polymer 2, currently in release candidate, includes support for legacy components so you can migrate applications piece by piece. The Angular team has also promised that breaking changes in Angular 2+ will be easier to migrate, and have hinted several times at a migration tool to streamline the changes.

While it’s hard to predict the future, I think that Polymer and the W3C standards it builds on will provide a more stable foundation for apps that need to be maintained for longer periods. 

Choosing development comfort over performance

We know that Polymer allows us to create fast web apps. But it turns out that Polymer by itself is too low-level to easily build big applications. There is clearly still a reason developers are selecting a framework like Angular instead of Polymer. Frameworks help developers structure applications and take care of things like routing, dependency injection, localization and other things you need to deal with when building a large application. Unfortunately, this developer convenience comes at a runtime performance cost to the end user. 

A good first step towards making Web Components suitable for building bigger and more performant apps would be an improved router. It would both help to give structure to apps and make it easier to create apps with tens or hundreds of views.  


In the last post of this series, I’ll take a look at why Google is working on two competing component-based tools for building web apps and what it might mean for the future of Web frameworks. 

Stay tuned.

Announcing Vaadin Prime

We’re happy to announce Vaadin Prime – the next generation of enterprise grade support for your team. You can now be a Vaadin user in three tiers; as an open source developer, a professional developer or a development team:

Vaadin Prime is for teams what Vaadin Pro is for individuals, starting from teams of 3 developers and including everything from Pro and Core.

We’re also giving unlimited access to all trainings available as an add-on to Pro and Prime – with over 30 new trainings that you can attend online, whenever it suits you best and as many times as you’d like to, for a flat subscription price.

Sign up with Prime before the end of April – get Trainings for free!

When you sign up for the Prime plan for a year, you get free trainings for the entire team for the first year. You will save $5,400 with a team of 3 persons and $18,000 with a team of 10. Sign up either online or contact our sales at sales@vaadin.com

For all our existing Pro Tools and Support customers – you can keep your current model or upgrade at any time by talking to our sales.

Get Vaadin Prime