21 improvements in Vaadin 8.0

Vaadin Framework 8 renewed essentially the whole data binding API and added dozens of smaller enhancements. Here is a collection of the most inspiring changes, that will give you a boost in your next Vaadin Framework project!

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 or 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. No need to wrap them with a Container. The old Container interface has been removed from the new API altogether, but is still available in the compatibility package for smooth transition.

 See video about it
V7
List<Person> persons = Backend.getPersons();
BeanItemContainer<Person> container =
    new BeanItemContainer(Person.class, persons);
Grid grid = new Grid();
grid.setContainerDataSource(container);
V8
List<Person> persons = Backend.getPersons();
Grid<Person> grid = new Grid<>(Person.class);
grid.setItems(persons);

ItemCaptionGenerator

A handy way to define a customized caption for options in select.

 See video about it
V7
List<Person> persons = Backend.getPersons();
ComboBox comboBox = new ComboBox();
BeanItemContainer<Person> bic =
    new BeanItemContainer<>(persons);
GeneratedPropertyContainer gpc =
    new GeneratedPropertyContainer(bic);
gpc.addGeneratedProperty("name",
    new PropertyValueGenerator<String>() {
  @Override
  public String getValue(
      Item i, Object id, Object o1) {
    Person p = (Person) id;
    return
        p.getFirstName() + " " + p.getLastName();
  }

  @Override
  public Class<String> getType() {
    return String.class;
  }

  @Override
  public Container.Filter
      modifyFilter(Container.Filter filter)
      throws UnsupportedFilterException {
    final SimpleStringFilter ssf =
        (SimpleStringFilter) filter;
    return new Container.Filter() {
      @Override
      public boolean passesFilter(
          Object itemId, Item item)
          throws UnsupportedOperationException {
        final Item genItem = gpc.getItem(itemId);
        String fullname =
            genItem.getItemProperty("name")
            .getValue().toString();
        return fullname.toLowerCase().startsWith(
          ssf.getFilterString());
      }
      @Override
      public boolean appliesToProperty(
          Object propertyId) {
        return "name".equals(propertyId);
      }
    }
  }
});
comboBox.setItemCaptionPropertyId("name");
comboBox.setContainerDataSource(gpc);
  Requires a lengthy code to create a custom item caption
V8
List<Person> persons = Backend.getPersons();
ComboBox<Person> comboBox = new ComboBox<>();
comboBox.setItemCaptionGenerator(
  p -> p.getFirstName() + " " + p.getLastName()
);
comboBox.setItems(persons);

Type safe value change listeners

ValueChangeListeners are now parameterized with the field value type, which makes wiring the business logic easier.

 See video about it
V7
comboBox.addValueChangeListener(evt -> {
    Person p =
        (Person) evt.getProperty().getValue();
    assert(p.getId() == 42);
});
  Requires casting
V8
comboBox.addValueChangeListener(evt -> {
    assert(evt.getValue().getId() == 42);
});

Grid column definition with type safe lambda expressions

A type safe mechanism to define columns is faster to use in IDE and much easier to understand.

 See video about it
V7
Grid grid = new Grid();
grid.setContainerDataSource(
  new BeanItemContainer<>(persons));
grid.removeAllColumns();
grid.addColumn("firstName");
grid.getColumn("firstName")
    .setHeaderCaption("First Name");
grid.addColumn("lastName");
V8
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 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.

 See video about it
V8
grid.setDataProvider(
    (sortorder, offset, limit) -> service.findAll(offset, limit),
    () -> service.count()
);

Inline Converter with lambda expression

You can define a convertor inline using two lambda expressions.

 See video about it
V8
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.

 See video about it
V7

Not supported. Validation is done only after conversion.

V8
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);
Animation demonstrating Vaadin 8 validation improvements

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.

 See video about it
Upgrading to Vaadin 8 using 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.

 See video about it
V8
// 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
});
Demonstrating support for HTML5 History API

HTML imports

HTML imports are a technology needed for utilizing Web Components. Since Vaadin 8, HTML imports are supported by the Framework, without need for special add-ons or low level JavaScript extensions.

 See video about it

Install the Web Component

bower install -save game-card

Implement the server side component

V8
@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)

V8
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";
Demonstrating Vaadin 8 with Polymer components

Add components and automatically expand them

The new addComponentsAndExpand method removes boilerplate code when adding components that need to be expanded.

 See video about it
V7
HorizontalLayout header =
    new HorizontalLayout(title, logout);
VerticalLayout root =
    new VerticalLayout(header, grid);

grid.setSizeFull();
root.setExpandRatio(grid, 1);
root.setSizeFull();
V8
HorizontalLayout header =
    new HorizontalLayout(title, logout);
VerticalLayout root = new VerticalLayout(header);
root.addComponentsAndExpand(grid);
Demonstrating new Vaadin 8 expand

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.

 See video about it
V8
Guidance in console about exception and how to fix the code

Improved defaults

In a major version, we had a chance to modify some default settings of 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.

 See video about it
V7
User user = new User();
user.setUsername(null); // set a null value

TextField textField = new TextField("Vaadin 7");
BeanFieldGroup<User> binder = new BeanFieldGroup<>(User.class);
binder.bind(textField, "username");
binder.setItemDataSource(user);
V8
User user = new User();
user.setUsername(null); // set a null value

TextField textField = new TextField("Vaadin 8");
Binder<User> binder = new Binder<>(User.class);
binder.bind(
  textField, User::getUsername, User::setUsername);
binder.setBean(user);
Demonstrating new Vaadin 8 null representation

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.

 See video about it
Vaadin 7 default margin and spacing
V7
Vaadin 8 default margins and spacing
V8

Eager field validation

Vaadin 8 supports using proper validation while the user is typing in fields. This improves the UX to desktop level.

 See video about it
V7
User user = new User();
TextField textField = new TextField("Vaadin 7");
textField.addValidator(
    new StringLengthValidator(
      "Too short", 8, 256, true));
textField.setNullRepresentation("");
BeanFieldGroup<User> binder =
    new BeanFieldGroup<>(User.class);
binder.setItemDataSource(user);
binder.bind(textField, "username");
V8
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 previous CPU consumed when listing large datasets

When listing in memory data, bean introspection is no more 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 was dropped from 233ms to 9ms.

Screenshot showing server latency in Vaaadin app with large in-memory dataset in version 7 and version 8.

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 layouting, 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.

Java logo

Modern Java server required

Vaadin 8 requires Servlet 3 container, which also rules out some legacy servers. Less hacks needed for old servers which results into 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.

V7

Not supported.

V8
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?