Beyond Angular and React: Building Web Apps with Vaadin
This article by Simon Martinelli first appeared in German in the JavaSpektrum Magazine 05/2020
Single-page web applications have today practically become standard. Angular, React, and Vue.js are the best-known representatives in this category of web frameworks. But would this client architecture apply to every use case? Or are there alternatives that would better suit themselves and would be less time- and cost-consuming in development?
Beyond SPAs: Why Consider Vaadin for Your Next Project
It all began with a new client asking for help modernizing his ERP product’s user interface. With his enterprise resource-planning, the client has been very successful in the german and swiss markets for over thirty years. Unfortunately, the then-current version of his product, based on Oracle Forms, was unusable in browsers and mobile devices.
Thus a project to find a replacement for Oracle Forms was initiated. A low-code tool was chosen, as this promised to enable a UI designer to generate significant portions of the user interface while reducing the programming effort through its abstract programming model. Unfortunately, the project did not progress as planned. An initial analysis showed that the low-code tool was a typical 80/20 solution. This means that the first 80 percent of the implementation was easy, as the tool covered the standard cases, but the remaining 20 percent were connected to large efforts, as many special cases had found their way into the ERP product during its long lifetime.
Another aspect that turned the project into a very challenging one was the application’s scope and dynamic character. The database contained around 1800 tables and views, 4600 procedures and functions, and over 100 user-defined types. The application’s menu contained around 800 entries. The forms and dialogues would change at runtime, depending on the user role and the data involved.
Clients also could adapt the user interface according to their requirements. Figure 1 illustrates this concept: The UI module is dynamically generated when accessed by the user, depending on the data and user rights. This functionality poses a certain challenge regarding the system’s performance, as a lot of data needs to be loaded from the database.
What was interesting, however, was what the low-code tool was generating: a web UI based on Vaadin. This piqued my interest. I remembered having used Vaadin in another project ten years ago. We quickly created a prototype using Vaadin itself, and after several discussions, the client decided to part ways with the low-code tool and directly use Vaadin as the project’s UI framework.
Vaadin: A Web Framework Tailored for Java Developers
Vaadin is a web framework that allows the development of Java web applications without requiring know-how in web technologies. Vaadin is open source and free. However, they offer subscriptions that contain courses, support, and commercial components, depending on the subscription level. The UI Designer and TestBench tools are also paid.
Figure 2 illustrates Vaadin’s history. The upper timeline shows the technologies and the lower one the versions. The most important changes were introduced in version 6, with the use of GWT, in version 8 with the integration of web components, and in version 10, with the new Flow architecture. Starting with version 15, Vaadin is offering three programming models. The best-known and most popular variant uses the Java API, which will also be discussed in this article.
Furthermore, Vaadin allows the creation of HTML templates, which can be integrated with Java code or, in the newest variant, the creation of the UI using TypeScript as the programming language and Vaadin flow as the communications interface. This last variant was created as Vaadin applications possess a state bound to the HTTP session. This prevents horizontal scaling at will. One nowadays also does not want to forgo offline capability. By separating client and server both disadvantages are remedied.
@Route public class HalloView extends VerticalLayout { public HalloView() { TextField textField = new TextField("Ihr Name"); Label label = new Label(); Button button = new Button("Gruß", e -> label.setText("Hallo, " + textField.getValue())); add(textField, label, button); } }
Everything Java, or what?
Listing 1 shows a simple “Hello World” example in Vaadin. If you want to make a page reachable via URL, it needs to be annotated with the @Route tag. The standard path is derived from the class name. In our case, HelloView becomes /hello. Also, note that the class is inheriting from VerticalLayout. Vaadin knows various layouts. VerticalLayout is a so-called ordered layout, which orders its components underneath each other. The view’s content is then created in the class constructor: A TextField, a Label, and a Button. A listener for the ClickEvent is registered with the Button, which results in a greeting displayed when clicked. The result is then identical to what is shown in figure 3.
This programming model is strongly reminiscent of AWT, Swing, or JavaFX, a large advantage for Java developers. Existing knowledge can be reused. HTML, CSS or JavaScript remain hidden.
Seamless Java Integration: Leveraging Existing Skills with Vaadin
Vaadin’s architecture was radically changed in version 10 (see fig. 4). The user sees no trace of GWT. Instead, a component called Vaadin Flow is now front and center. It offers a typical Java-component API on the server side. Web components are used on the client side. Vaadin Flow takes care of the bidirectional communication between the browser and server, offering data binding in both directions. This means that changes on one side are automatically synchronized on the other side. This also allows for pushing Java to the browser. For this purpose, the Atmosphere framework [Atmosphere] is used. It ensures that push also works when no WebSockets are available.
The major advantage, in comparison to architectures that are used in single-page applications, is that no REST API is necessary. This does away with a large part of the implementation effort and the entire testing effort on the API layer. The integration of the Java backend becomes significantly simpler and more efficient. Additionally, the developers remain on the Java stack and neither need to learn another programming language nor another build environment.
Routes
A significant difference to earlier Vaadin versions is the concept of routing. This moves Vaadin closer to more conventional web applications.
@Route public class HasParameterView implements HasUrlParameter<String> { @Override public void setParameter(BeforeEvent event, String parameter) { QueryParameters params = event.getLocation().getQueryParameters(); } }
The route’s path is derived from the class name, omitting the suffix View. This would be /hasparameter in listing 2. When declaring the path can be defined in the annotation. If an empty string is defined as the path, as with @Route(“ “), or if the class is called MainView, then this is bound to the root path. It is also possible to nest paths by separating sections with /.
As one can see in Listing 2, routes can possess URL parameters, and one can access query parameters. This allows single pages of a Vaadin application to be called directly and the setting of bookmarks. To use URL parameters, the HasUrlParameter interface needs to be implemented. The parameter type is then defined via the generic type and passed to the method setParameter(). The Location object, requested with the BeforeEvent, is used to access the Query parameter.
Components
Vaadin comes with many high-quality components, each of which can be adapted to the requirements using theming. There is also a large directory of third-party components. All Vaadin components are web components. Vaadin also offers the possibility to integrate existing web components and generate one’s own. According to webcomponents.org [WebComponents], web components are a “…set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps.“ A major advantage of web components is that these can also be used independently of Vaadin.
PWAs: Building Progressive Web Apps with Vaadin
With Vaadin, one can also generate Progressive Web Apps (PWAs). The three essential pillars of a PWA are the web app manifest, the service worker, and the push notifications. The web app manifest contains all the information about an application, such as the name, icon, and description. This allows the PWA’s installation on a device. The service worker provides the necessary functionalities for the PWA’s offline capability and its push notifications.
@Push @PWA(name = "JavaSpektrum Application", shortName = "JS App", description = "This is an example Vaadin application") @Route public class MainView extends VerticalLayout { }
Listing 3 shows a minimal configuration for PWA and push. Upon starting the application, a message is shown in the browser window to indicate that the application is installable (see fig. 5). The browser installs the application after the user clicks “Install” and acknowledges the message. When one opens the application, it is started in a browser window that lacks the menu and the address bar, thus looking like a native application. The offline support can be configured via an offline page and the service worker. Vaadin generates an sw.js file, which contains the service worker’s code and can be adapted to one’s own requirements.
Streamlining Data Management with jOOQ
As initially mentioned, the database of our ERP system contained 1800 tables and 4600 procedures. We had to find a suitable method to access these. JDBC as the database interface was set, but was an O/R mapper, such as Hibernate, the right approach here? How much effort would the mapping of 1800 entities constitute? And how can 4600 procedures be called in a typesafe manner? The database would require type information, as the UI was generated at runtime. How could this be accessed? All these questions could, fortunately, be answered using a single framework: jOOQ [jOOQ].
jOOQ integrates SQL in Java and enables the typesafe use of SQL. jOOQ generates Java code based on the database’s metadata and offers a Domain Specific Language (DSL) in the form of a Fluent API, to formulate typesafe SQL and execute procedures. The Codegen Maven plugin can be configured to generate code as shown in listing 4.
The basis for the generation can either be formed by directly using the database via a JDBC connection or by accessing DDL scripts, as shown in listing 4. Listing 4 only shows a section of the jOOQ code generator’s configuration possibilities. In the example, we are using FlyWay migrations as the base. The result is depicted in figure 6. A class is generated for all database objects. In the example, we have an Employee and a Department table and a VEmployee view.Additionally, three Record classes are also generated. A record represents
<plugin> <groupId>org.jooq</groupId> <artifactId>jooq-codegen-maven</artifactId> <configuration> <generator> <database> <name>org.jooq.meta.extensions.ddl.DDLDatabase</name> <properties> <property> <key>scripts</key> <value>src/main/resources/db/migration/*.sql</value> </property> </properties> </database> <target> <packageName>io.seventytwo.demo.database</packageName> <directory>target/generated-sources/jooq</directory> </target> </generator> </configuration> </plugin>
Listing 5 shows a section of the code for the generated class Department. The class that jOOQ generates contains all of this table’s metadata. In the generated code, we can also find all of the table’s columns with all the information on the name, type, whether the column is nullable and whether it is a primary key column. Even the comments in the database are included. This is very helpful and increases efficiency, as the developer does not need to check this information in the database. On the other hand, this information can also be used to validate user input.
public final TableField<DepartmentRecord, Integer> ID = createField(DSL.name("ID"), INTEGER.nullable(false).identity(true), this, ""); public final TableField<DepartmentRecord, String> NAME = createField(DSL.name("NAME"), VARCHAR(255).nullable(false), this, "");
// Insert dsl.insertInto(DEPARTMENT) .columns(DEPARTMENT.NAME) .values("IT") .execute(); // Select All List<EmployeeRecord> records = dsl.selectFrom(EMPLOYEE).fetch(); // Select mit Projektion List<Record1<String>> records = dsl.select(EMPLOYEE.NAME) .from(EMPLOYEE) .join(DEPARTMENT).on(DEPARTMENT.ID.eq(EMPLOYEE.DEPARTMENT_ID)) .where(DEPARTMENT.NAME.eq("IT")) .fetch();
Listing 6 contains examples of how these classes enable the writing of typesafe SQL, i.e., SQL verified at compilation time. It is important to mention that jOOQ implements the Java code in SQL, considers the respective database product, and emulates functionality as necessary. This results in independence that could allow for the exchange of the used database product. jOOQ supports all modern databases.
Displaying records
There is a vital main component to developing data-centric applications: The grid. Vaadin has focused on this component and offers excellent paging, sorting, and filtering support.
The example in Listing 7 creates a grid based on the VEmployeeRecord class. When creating a column, a function is passed, which is used to access the data. Columns are made sortable by setting the SortProperty.
Grid<VEmployeeRecord> grid = new Grid<>(); grid.addColumn(VEmployeeRecord::getEmployeeId) .setHeader("ID") .setSortProperty("ID"); grid.addColumn(VEmployeeRecord::getEmployeeName) .setHeader("Name") .setSortProperty(V_EMPLOYEE.EMPLOYEE_NAME.getName()); grid.addColumn(VEmployeeRecord::getDepartmentName) .setHeader("Department") .setSortProperty(V_EMPLOYEE.DEPARTMENT_NAME.getName());
In Vaadin, the DataProvider takes care of the data. Two variants are discernible, depending on where the data are held. An in-memory DataProvider, to which data are passed in a collection, is suitable
for small amounts of data. The CallbackDataProvider, which supports lazy loading, is suitable for when there are large numbers of datasets. In the lazy loading mode, the grid is automatically reloaded during scrolling. A FetchCallback, which returns a stream of datasets, and a CountCallback, which returns many datasets, need to be passed to the CallbackDataProvider. A ValueProvider, which has to return a unique key value, can also be passed as an optional third parameter. This key value allows for the identification of lines, e.g., to carry out an update. Without this ValueProvider the equals() and hashCode() data classes are used.
Both callback methods contain a query as their parameter. This query contains offset, limit, sorting, and if necessary, a filter object and is used to constrain and sort the result set. Listing 8 shows how well Vaadin and jOOQ integrate. A query for data and one for several datasets are generated. With withConfigurableFilter() a ConfigurableFilterDataProvider can be created from this for a DataProvider. This DataProvider provides the method setFilter(), which allows results to be constrained. Listing 9 shows how, like with a TextField, a jOOQ Condition is generated on changes and is passed to the setFilter() method. Condition is a jOOQ class used in the queries’ WHERE conditions.
TextField filter = new TextField("Filter"); filter.setValueChangeMode(ValueChangeMode.EAGER); filter.addValueChangeListener(event -> { if (StringUtils.isNotBlank(event.getValue())) { dataProvider.setFilter(DSL.upper(V_EMPLOYEE.EMPLOYEE_NAME) .like("%" + event.getValue().toUpperCase() + "%")); } else { dataProvider.setFilter(null); } });
Of course, any data access layer, such as a service or a repository, can be used. This is, however, unnecessary in many cases, and it only signifies a larger effort with low returns.
Simplified Data Binding
The second fundamental element in an application form. Vaadin knows the concept of data binding. In Vaadin, the class Binder is responsible for this. It binds a Java object’s values to UI components and takes care of conversion and validation. A binding can be writable or read-only. For all fields not of the type String, a converter must be specified. Common converters, such as ones that convert strings to numbers or date types, are already integrated into Vaadin. But writing your converter is also very simple.
Listing 10 also shows how one can mark a field as a mandatory field. This leads to the respective components being marked accordingly. The method setBean() needs to be called to bind an object. The method validate() can then be used to check whether the modified object possesses valid values. If the values are invalid, the respective component is marked, and a validation message is issued.
Binder<EmployeeRecord> binder = new Binder<>(); TextField id = new TextField("ID"); binder.forField(id) .withConverter(new StringToIntegerConverter("Darf nicht leer sein")) .bind(EmployeeRecord::getId, null); TextField name = new TextField("Name"); binder.forField(name) .asRequired() .withValidator(s -> s.length() >=3, "Name ist zu kurz") .bind(EmployeeRecord::getName, EmployeeRecord::setName);
Effortless Integration & Testing
Vaadin offers integrations for Spring Boot and Jakarta EE. In this case, a route to a Spring Bean or a CDI Bean is automatically created. In both integrations, Vaadin knows an important additional scope or context. The UIScope is valid as long as the user has the application open in his browser. This allows the same application to be independently opened multiple times in the same browser. The applications can be deployed in an application server as web archives (WAR), or be run standalone as Spring Boot applications.
The UI code is completely written in Java and can thus easily be tested using unit tests. Vaadin’s commercial version encompasses the Vaadin TestBench, a tool that can be used to create and execute bowser-based integration tests. Under its hood TestBench uses Selenium with WebDriver. A test for our initial “Hello World” example is shown in listing 11. Vaadin provides a jQuery-like intax to access the elements.
@Test public void showHalloJavaSpektrum() { TextFieldElement textField = $(TextFieldElement.class).first(); textField.setValue("JavaSpektrum"); textField.sendKeys(Keys.ENTER); LabelElement label = $(LabelElement.class).first(); Assert.assertEquals("Hallo JavaSpektrum", label.getText()); }
Beyond CRUD: Building Feature-Rich Web Apps with Vaadin
Today, many applications created as SPAs with JavaScript frameworks offer little more functionality than simple Create, Read, Update and Delete (CRUD). The effort required to develop a client/server application is very high. Either a separation between the frontend and backend teams must occur or so-called full-stack developers must master both technology stacks.
With Vaadin, a web framework allows Java developers to rapidly and efficiently develop appealing web applications. Especially the circumstance that no REST API is required means large savings in the development and maintenance of the application. The database can be efficiently connected in a typesafe manner in the data access layer, thanks to jOOQ. There is no detour via an O/R mapping, which in many cases would be unnecessary and prevent the use of the database’s full functional capacity. In this sense: KISS (Keep It Simple and Stupid)!
Ready to ditch the complexity of SPAs and build efficient Java web apps? Contact me today for a consultation on your Vaadin project!
Related Posts:
- Spring boot, angular and JWT authentication
- Type Safe SQL in Java
- Vaadin and jOOQ: Match Made in Heaven