Migration example - Bookstore Starter

This document shows an example of migration from a Vaadin 8 app to Vaadin 14. The migration process is done step-by-step and can be seen through the history of its GitHub repository. The idea is to keep the application compilable in order to be able to see the result of migrating steps.

Step 1 - Initial Vaadin Flow configuration

Maven

First of all, required maven dependency must be added to pom.xml. The Vaadin 8 dependencies, except vaadin-themes, are kept for now and will be eliminated after the whole application is migrated. The only Vaadin platform dependency is the following:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin-bom</artifactId>
      <type>pom</type>
      <scope>import</scope>
      <version>${vaadin.version}</version>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
     <groupId>com.vaadin</groupId>
     <artifactId>vaadin-core</artifactId>
  </dependency>
</dependencies>

Starting from Vaadin 14, the vaadin-maven-plugin should be configured for development time to make sure that all the needed resources are available for the development of the project. While it is not mandatory to have the plugin configured for all projects, it is needed for this migration so that necessary files are generated:

<build>
  <plugins>
    <plugin>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin-maven-plugin</artifactId>
      <version>${vaadin.version}</version>
      <executions>
        <execution>
          <goals>
            <goal>prepare-frontend</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

In this document, migrating custom components and extensions are not covered. So, the widgetset module that includes the following extensions is removed.

  • AttributeExtension

  • ResetButtonForTextField

UI class and Servlet configuration

  • Both UI class and Servlet configuration are optional in Vaadin Flow. However, we can keep them to leverage them in some cases e.g. controlling user access.

  • Since the components are different, the DOM structure has changed. The new components are based on web components and a new theme. Thus the theming is discussed later.

  • In Vaadin Flow the locale is set automatically based on user preferred locale. So, setLocale can be removed.

  • Best practice for setting page title is using PageTitle annotation on each view. So, getPage().setTitle is removed from MyUI::init.

  • All packages names in Vaadin Flow start with com.vaadin.flow. One way to correct them is to remove all import statements starting by com.vaadin and reimport Vaadin Flow classes. For example some equivalent classes in Vaadin Flow are:

  • com.vaadin.ui.UIcom.vaadin.flow.component.UI

  • com.vaadin.server.VaadinRequestcom.vaadin.flow.server.VaadinRequest

  • com.vaadin.ui.TextFieldcom.vaadin.flow.component.textfield.TextField

  • com.vaadin.ui.VerticalLayoutcom.vaadin.flow.component.orderedlayout.VerticalLayout

Test page

In order to verify that Vaadin Flow setup has been done correctly, a simple HelloWorldPage like the following can be added.

import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;

@Route("")
@PageTitle("My")
public class HelloWorldPage extends Div {
   public HelloWorldPage() {
       this.add(new H1("Hello World!"));
   }
}

@Route("") shows that root path should be routed to this page. After running the application by mvn jetty:run, the “Hello World!” message can be seen in the browser by entering this address: http://127.0.0.1:8080.

Step 2 - Access Control and Login Screen

VaadinServletConfiguration and UI class

In Vaadin Flow, defining a Servlet class is optional. So, we don’t have to create an extended class of VaadinServlet, unless we need to change some configuration. Having a UI class is optional too and this class can be removed as well because the UI class is created by the framework. However, we may have some tasks assigned to our UI class e.g. controlling access. In this example access control is moved to a more suitable place which is described in the following section.

Access Control

BeforeEnter event of UI class is a good place to control access and there is another event named UIInit in VaadinService class that is fired whenever a UI is created. In order to leverage these events, we can create a class extended from VaadinServiceInitListener and add required code in serviceInit method. The result looks like the following piece of code:

public class BookstoreInitListener implements VaadinServiceInitListener {
   @Override
   public void serviceInit(ServiceInitEvent initEvent) {
       initEvent.getSource().addUIInitListener(uiInitEvent -> {
           uiInitEvent.getUI().addBeforeEnterListener(enterEvent -> {
               // Controlling access can be done here.
           });
       });
   }
}

MyUI class had an instance of BasicAccessControl and other classes used it via its accessor; now after MyUI class is eliminated, there must be another provider for AccessControl implementation. The selected solution here is using a factory class (AccessControlFactory).

CurrentUser class is also needed to change because it is used in BasicAccessControl class. We need to apply new packages names of Flow that start with com.vaadin.flow. The same should be done in the next steps of migration.

LoginScreen

This is the first UI screen migrated to 14. The following items describe what needs to be done in the migration process:

  • Instead of CssLayout another equivalent component must be used e.g. FlexLayout or a simple Div.

  • Equivalent of addComponent method is add method.

  • setWidth method in Flow has only one String parameter that includes both measurement unit and width as a number e.g. “15em” or “310px”.

  • Route annotation determines the URL associated with this screen.

  • Predefined style changes to components in 14 are referred to "theme variants", and those change the theme attribute of the components instead of the className. So, addStyleName(String) can be replaced with addThemeVariants(…​). The available theme variants for components are showcased in the component demos. Changes in theming from V8 to Vaadin platform is described here.

  • New FormLayout has a method named addFormItem takes a component as a parameter and in addition to adding it to the form, it adds a label beside the component as well.

  • Instead of Button::setClickShortcut the API is now Button::addClickShortcut;.

Some other changes that have been done are not related to Vaadin framework migration process; however, it is a good idea to do such refactorings at the same time as migration.

Step 3 - Menu, MainScreen and AboutView

As explained before, instead of CssLayout, FlexLayout is used.

Navigator class is removed in Flow and this is one of many changes in routing and navigation since version 8. So, navigator field is removed from Menu. In addView method it can be seen that navigation is done by RouterLink component.

At this stage, a pretty look is not aimed for. It will be made nicer in later steps.

MainScreen

In Vaadin 8 version there is a CssLayout that acts as a view container and navigation between different views is done inside the CssLayout. In Vaadin Flow, parent layouts can be defined using a newly introduced RouterLayout interface. Since MainScreen is used as a layout for other views, it must implement RouterLayout interface.

AboutView

Layout of views can be specified in Route annotation like this @Route(value = "About", layout = MainScreen.class). We don’t need the HelloWorldPage anymore, so it is removed and since it’s good to have a route to root path, RouteAlias annotation is used to add a secondary path for AboutView.

Another thing worth mentioning here is that in Vaadin platform, a component named Icon is added and can be created by calling create method of VaadinIcon enum.

Here is the link to see the changes in step 3.

Step 4 - Product Grid

DataProvider

In Vaadin platform, when DataProvider::fetch method is overridden, query.getOffset() and query.getLimit() must be used to fetch a specific chunk of data. If they are not used it shows that the returned data is incorrect and unexpected. To avoid such mistakes in implemented code, Vaadin platform throws an IllegalStateException to show us what is wrong. So, ProductDataProvider::fetch is fixed in order to use specified offset and limit. The data provider documentation for Vaadin platform can be found here.

ProductGrid

The following items briefly describe some of the changes in ProductGrid.

  • There is no HtmlRenderer in Vaadin platform and it must be replaced by other renderers such as TemplateRenderer or ComponentRenderer. In this migration, TemplateRenderer is used. More info and guidance about all kinds of renderers can be found in "Using Renderers" section of Grid document. In TemplateRenderer, apart from HTML markup, Polymer data binding notation can also be used. In ProductGrid, there are three TemplateRenderers:

    • Price and StockCount columns leverage TemplateRenderer to align their text to the right.

    • Availability column template uses a Vaadin component named iron-icon to show a circle colored based on availability value. In order to set different styles to the circle, three CSS classes with equivalent names to three values of availability (Available, Coming and Discontinued) are defined in a CSS file (grid.css). Also, the dependency of the grid on the CSS file is defined by adding CssImport annotation to ProductGrid class.

  • Grid.Column::setCaption method is renamed to setHeader.

  • setFlexGrow method is called for each column to set grow ratios of them.

SampleCrudView

This is the page that includes ProductGrid and ProductForm and since ProductForm is going to be migrated in the next step, the parts of the code related to it are commented. Like in the other views, a Route annotation is added here with the "Inventory" value. Also, as this view is the main view of the project, the route to the root path, the RouteAlias annotation, should be moved here. Other changes in SampleCrudView are the following items.

  • getElement().getThemeList()::add is used to add a theme variant to a component. An improved API for this has been released in V12.

  • In Vaadin 8, to get the parameters passed via the URL, View interface must be implemented and the enter method must be overridden. In Vaadin platform, there is an interface named HasUrlParameter that does the job. It is generic, so parameters are safely converted to the given types. More information about URL parameters can be found here.

  • Instead of using HorizontalLayout::setExpandRatio, HorizontalLayout::expand method is used.

Here is the link to see the changes in step four.

Step 5 - Product Form

Since after this step, all Java code is migrated to Vaadin platform, it is time to remove Vaadin 8 dependencies. Besides, keeping both versions may cause some conflicts in their dependencies e.g. jsoup. So, vaadin-server and vaadin-push are removed from pom.xml. Other changes in this step are as follows.

ProductForm Design

The following items are some of the changes from Vaadin 8 to Vaadin platform in design files.

  • In Vaadin 8, Vaadin Designer uses HTML markups to store designed views and they are stored in files with html extension. However, the tags that are used by Vaadin Designer are not standard HTML tags. So, these html files cannot be correctly shown and rendered by browsers. While in Vaadin platform, Polymer template is used to define views and Vaadin Designer also uses it to store designed views.

  • Prefix of the Vaadin components names is changed from v to vaadin.

  • For customizing the look and feel of the components using the provided theme variants, the variants are applied with the theme attribute, instead of the style-name (class name). E.g.

Vaadin 8 version:

<v-button style-name="primary" _id="save">Save</v-button>

Vaadin platform version:

<vaadin-button theme="primary" id="save">Save</vaadin-button>

ProductForm Java Class

ProductFormDesign class is removed and its content is moved to ProductForm class. Actually, this is the recommended pattern in Vaadin platform and it is also supported by Vaadin Designer. In Vaadin 8, Vaadin Designer keeps two classes, a superclass for designer generated code and an inherited class for the code implemented by the developer. The following items are some of the changes in ProductForm.

  • JsModule and Tag annotations are the required annotations to connect ProductForm class to its design file, ProductFormDesign.html. And unlike Vaadin 8, reading the design file is done automatically and there is no need to call Design.read.

  • Id annotation is used to connect fields to their equivalents in the associated polymer template.

  • In ComboBox, setEmptySelectionAllowed method is renamed to setAllowCustomValue.

ErrorView

Router Exception Handling in Vaadin Flow is described here. Applications can have different views for catching different exceptions. For example, ErrorView catches NotFoundException that is thrown when something goes wrong while resolving navigation routes. And unlike Vaadin 8, there is no need to register ErrorView in a navigator or something like that. It is automatically detected and is used by Flow.

SampleCrudLogic

Apart from some cleaning, a small change that is worth mentioning is the change in how the URL of the browser is updated. In Vaadin 8, page.setUriFragment is called and the new URL must be constructed and passed as a parameter. While in Vaadin Flow, it is done more elegantly; navigate method of UI class is called and the view parameter is passed as a parameter to navigate method.

Step 6 - Production Mode

In Vaadin 14 the production mode is recommended to be enabled by is adding a profile to pom.xml. All old V8 related production build configuration can be removed. The following code shows the required configuration for enabling a production build in 14 when running the command mvn package -Pproduction:

<profiles>
  <profile>
    <!-- Production mode is activated using -Pproduction -->
    <id>production</id>
    <properties>
      <vaadin.productionMode>true</vaadin.productionMode>
    </properties>

    <dependencies>
      <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>flow-server-production-mode</artifactId>
      </dependency>
    </dependencies>

    <build>
      <plugins>
        <plugin>
          <groupId>com.vaadin</groupId>
          <artifactId>vaadin-maven-plugin</artifactId>
          <executions>
            <execution>
              <goals>
                <goal>build-frontend</goal>
              </goals>
              <phase>compile</phase>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

For further details on production mode in 14, you can read here.

Step 7 - Theming the application

There is currently no example on migrating from an existing Vaadin 7 or 8 theme to Vaadin 14 Lumo theme. The component set is different, so the styling cannot be applied out-of-the-box to newer versions.

Thus it is recommended to switch to using the new Lumo theme, and customizing the look and feel on top of that. For more information, check the Themes and Styling documentation.