21 Improvements in Vaadin 8
Vaadin 8 was a major overhaul, deprecating some old features to introduce new support for JDK 8, modern Java APIs, improved type safety, and easier backend lazy loading.
Vaadin Framework 8 renewed essentially the whole data binding API and added dozens of enhancements. Here is a list of the most important changes that will give you a boost in your next Vaadin Framework projects.
New Modern Java APIs
The main feature in Vaadin 8 is core API modernization. We now fully take advantage of parameterization, lambdas, etc. Upgrading to Vaadin 8 is still easy as legacy APIs are still available in a compatibility package.
Pass Lists of Entities Directly to Grid and Selects
The most common way to populate Grid
and various select components has become much simpler and more typesafe. Just pass the list or array of your model objects directly to your Grid
or select component:
List<Person> persons = Backend.getPersons();
Grid<Person> grid = new Grid<>(Person.class);
grid.setItems(persons);
ItemCaptionGenerator
ItemCaptionGenerator
is a handy way to define a customized caption for options in selects:
List<Person> persons = Backend.getPersons();
ComboBox<Person> comboBox = new ComboBox<>();
comboBox.setItemCaptionGenerator(p -> p.getFirstName() + " " + p.getLastName());
comboBox.setItems(persons);
Typesafe Value Change Listeners
ValueChangeListeners
are now parameterized with the field value type, which makes wiring the business logic easier:
comboBox.addValueChangeListener(evt -> assert(evt.getValue().getId() == 42));
Grid Column Definition With Typesafe Lambda Expressions
A typesafe mechanism to define columns that is faster to use in-IDE and much easier to understand:
Grid<Person> grid = new Grid<>();
grid.setItems(persons);
grid.addColumn(Person::getFirstName).setCaption("First Name");
grid.addColumn(Person::getLastName).setCaption("Last Name");
Easier Lazy Loading of Data From the Backend
Previously, for a memory efficient lazy loading solution you needed to implement a complex Container
interface. Now, in most cases, you can do a lazy binding to your backend using two lambda expressions:
grid.setDataProvider(
(sortorder, offset, limit) -> service.findAll(offset, limit),
() -> service.count()
);
Inline Converter With Lambda Expressions
You can define a converter inline using two lambda expressions:
new Binder<Person>().forField(textField)
.withConverter(
textToInteger -> Integer.parseInt(textToInteger),
integerToText -> integerToText.toString()
)
// or with method references:
// withConverter(Integer::parseInt, Object::toString)
.bind("age");
Validators Can Be Defined Both Before and After Conversion
You can define field validators both based on the value type of the field or the converted value that will be used in your business objects:
new Binder<Person>().forField(tf)
.withValidator(str -> str.length() == 4, "Must be 4 chars")
.withConverter(new StringToIntegerConverter("Must be Integer"))
.withValidator(integer -> integer.equals(2017), "Wrong date")
.bind(Person::getBirthYear, Person::setBirthYear);
Legacy APIs Still Supported
For components with dramatic changes, we have preserved the legacy components in a compatibility package. With the migration tool you can upgrade to Vaadin 8 in a snap and start using new improved APIs gradually. Update the version of the Vaadin Maven Plugin to the latest available version and run:
mvn vaadin:upgrade8
Support for HTML5 History API
History API makes it possible for single-page web apps to change the location of the browser and this way support proper deep linking, back button and search engines, without hashbang ("#!" in URL) haxies:
// push state:
Button button = new Button("Go to page 1");
button.addClickListener(e -> {
// URL will change to .../page1
Page.getCurrent().pushState("page1");
});
// pop state:
Page.getCurrent().addPopStateListener(event -> {
String uri = event.getUri();
// ... update the UI accordingly
});
HTML Imports
HTML imports are a technology needed for utilizing web components. Since Vaadin 8, HTML imports are supported by the Framework, without the need for special add-ons or low-level JavaScript extensions:
bower install -save game-card
// implement the server-side component:
@JavaScript("GameCard.js")
@HtmlImport("vaadin://bower_components/game-card/game-card.html")
public class GameCard extends AbstractJavaScriptComponent {
public GameCard(String symbol, String rank) {
callFunction("setCard", symbol, rank);
}
}
// implement the client-side component (GameCard.js):
com_example_GameCard = function () {
var element = this.getElement();
this.setCard = function (symbol, rank) {
element.set("symbol", symbol);
element.set("rank", rank);
};
};
com_example_GameCard.tag = "game-card";
Add Components and Automatically Expand Them
The new addComponentsAndExpand
method removes boilerplate code when adding components that need to be expanded:
HorizontalLayout header = new HorizontalLayout(title, logout);
VerticalLayout root = new VerticalLayout(header);
root.addComponentsAndExpand(grid);
Helpful Guidance in Exception Messages
Exceptions thrown by the framework often contain a verbose explanation in the error message about how to fix the code:
Improved Defaults
Because Vaadin 8 is a major version, the team had a chance to modify the default settings of some commonly used components. Less to configure, more time to think about your actual UI.
Null Values Are Displayed as Empty Strings by Default
In earlier Vaadin versions, TextField
rendered null values as "null", which may have been a good choice for developers, but useless for end users. Now the default is an empty string as it should be:
Ordered Layouts Now Have Better Defaults
Vaadin UIs now look better than ever by default. Margin and spacing defaults have been adjusted in most commonly used layouts and you don't need to call setMargin
and setSpacing
that often:
With Vaadin 7 | With Vaadin 8 |
Eager Field Validation
Vaadin 8 supports using proper validation while the user is typing in fields. This improves the UX to desktop level:
User user = new User();
TextField textField = new TextField("Vaadin 8");
Binder<User> binder = new Binder<>();
binder.setBean(user);
binder.forField(textField)
.withValidator(new StringLengthValidator("Too short", 8, 256))
.bind(User::getUsername, User::setUsername);
Improved Performance
The new data binding code is much easier and productive for developers, but it also saves quite a bit of memory and CPU.
Up to 10 Times Less Overhead in In-Memory Listings
The new data access mechanism consumes multitudes less memory when listing in-memory data. Previously, the most commonly used Container
, BeanItemContainer
, eagerly created a wrapper for each and every domain object you wanted to list. Now extra memory is not wasted for non-visible objects at all.
A Fraction of the Previous CPU Consumed When Listing Large Datasets
When listing in-memory data, bean introspection is no longer done at all or done on demand. This saves a lot of CPU cycles when listing a large number of rows in your data grids. In a simple test with 100,000 entities listed in a Grid
, the server CPU time needed to serve the view dropped from 233ms to 9ms.
Built for the Future
There are some changes that might actually remove "features," but which allow us to add exciting new features in upcoming minor releases.
Modern Web Browsers Supported
By dropping legacy browser support, we can start using more modern web technologies, like CSS3 flexbox layout, and add client-side optimizations, which were not possible before.
Java 8 or Newer Required
Using JDK 8 features in the API allows us to build features faster and in a more elegant way. Many of the API improvements presented above wouldn't have been possible while supporting JDK 7.
Modern Java Server Required
Vaadin 8 requires a Servlet 3 container, which also rules out some legacy servers. Less hacks needed for old servers, which results in a more maintainable code base.
New Hook for Add-ons to Configure Themselves
Vaadin add-ons can now hook to application initialization using VaadinServiceInitListener
interfaces, built on Java's standard ServiceLoader mechanism. This can make the usage of certain Vaadin add-ons much easier:
// file META-INF/services/com.vaadin.server.VaadinServiceInitListener:
com.example.MyListener
// file: MyListener.java:
import com.vaadin.server.ServiceInitEvent;
import com.vaadin.server.VaadinServiceInitListener;
public class MyListener implements VaadinServiceInitListener {
@Override
public void serviceInit(ServiceInitEvent event) {
// Configure service, add session init listeners,
// request handlers etc...
// ...
}
}
Are you ready to build your first Vaadin 8 app? Read the tutorial or visit the documentation site.
Comments