Академический Документы
Профессиональный Документы
Культура Документы
8
1
21 IMPROVEMENTS
in Vaadin 8
by Alejandro Duarte
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.
1
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 populateGrid and various select components has
become much simpler and more typesafe. Just pass the list or array of your
model objects directly to yourGrid or select component:
List<Person> persons = Backend.getPersons();
grid.setItems(persons);
ItemCaptionGenerator
ItemCaptionGeneratoris a handy way to define a customized caption for options
in selects:
List<Person> persons = Backend.getPersons();
comboBox.setItems(persons);
grid.setItems(persons);
2
grid.addColumn(Person::getFirstName).setCaption("First Name");
grid.addColumn(Person::getLastName).setCaption("Last Name");
() -> service.count()
);
.withConverter(
// withConverter(Integer::parseInt, Object::toString)
.bind("age");
3
.bind(Person::getBirthYear, Person::setBirthYear);
button.addClickListener(e -> {
Page.getCurrent().pushState("page1");
});
// pop state:
Page.getCurrent().addPopStateListener(event -> {
});
HTML Imports
HTML imports are a technology needed for utilizing web components. Since
Vaadin 8, HTML imports are supported by the Framework, without theneed
for special add-ons or low-level JavaScript extensions:
bower install -save game-card
4
// implement the server-side component:
@JavaScript("GameCard.js")
@HtmlImport("vaadin://bower_components/game-card/game-card.html")
com_example_GameCard = function () {
element.set("symbol", symbol);
element.set("rank", rank);
};
};
com_example_GameCard.tag = "game-card";
root.addComponentsAndExpand(grid);
5
Improved Defaults
Because Vaadin 8 is a major version, the team had a chance to modify the de-
fault 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:
6
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
callsetMargin andsetSpacing that often:
With Vaadin 7 With Vaadin 8
binder.setBean(user);
binder.forField(textField)
.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 list-
ing in-memory data. Previously, the most commonly usedContainer,BeanItem-
Container, 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 thePrevious CPU Consumed When List-
ing 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 cycleswhen listing a large number
of rows in your data grids. In a simple test with 100,000 entities listed in
aGrid, the server CPU time needed to serve the view dropped from 233ms to
9ms.
Built for the Future
7
There are some changes that might actually remove "features," but which al-
low 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 serv-
ers. Less hacks needed for old servers, which results in amore maintainable
code base.
New Hook for Add-ons to Configure Themselves
Vaadin add-ons can now hook to application initialization usingVaadinServ-
iceInitListener interfaces, built on Java's standard ServiceLoader mecha-
nism. 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;
@Override
// ...
8
9
2
CHAPTER
VIRITIN
Commodo hendrerit frigilla lucus mauris
10
Lazy loading in Viritin
Clone in Desktop
Viritin contains sophisticated solutions for "lazy loading" data from your backend services.
You can/should use this technique when you connect a Table/ComboBox/Grid to a large
set of rows/options. Using the lazy loading features in Viritin have been build as simple as
possible, so it is easy to connect it to variety of "backends", but it is still better performing
than e.g. JPAContainer.
Lazy loading is build using ListContainer and LazyList classes, and their sub-classes, but
most often you don't need to use those classes directly. Instead you can, and it is sug-
gested, that just bind your data source directly to the UI components.
Generally you'll have to implement two interfaces, most commonly with lambda expres-
sions. CountProvider needs to return the amount of items available and PagingProvider
will return a java.util.List of items, starting from a certain index.
list.lazyLoadFrom(
// entity fetching strategy
(firstRow, asc, sortProperty) -> repo.findAllBy(
new PageRequest(
firstRow / PAGESIZE,
PAGESIZE,
asc ? Sort.Direction.ASC : Sort.Direction.DESC,
// fall back to id as "natural order"
sortProperty == null ? "id" : sortProperty
)
),
// count fetching strategy
() -> (int) repo.count(),
PAGESIZE
);
If you use the variations without page size, the default page size is 45. Note, that by de-
fault, that is too small for full sized Grid, causing Grid to do random access to the back-
end and causes issues with the lazy loading strategy. If you see too many queries to your
backend, try increasing the page size.
Note: If you use the lazy listing in many places in your application, create a custom class
to abstract the lazy loading code. E.g. PersonListing extends MTable in this case.
11
Filtering listing
If your UI has a filtering for the listing, you can simply use the filtering data directly in your
binding code. To notify the components of changed data, also call refreshRows() method,
that will clear the cache in components.
LazyComboBox
Lazy loading in the LazyComboBox works with similar principles as with MTable. The dier-
ence is, that both methods in the functional interfaces that you need to implement, gets an
additional filter (String) parameter. Below you can see an example usage with classes (but
naturally your can and should use lambdas if you can). As you are responsible for doing
the filtering, you can do pretty much what ever you want with the filtering, simple starts
with filtering is only one option. In this example we also use a CaptionGenerator to com-
bine firstName and lastName properties to be shown on the UI.
final LazyComboBox.FilterablePagingProvider filterablePagingProvider = new
LazyComboBox.FilterablePagingProvider() {
@Override
public List findEntities(int firstRow, String filter) {
return service.findPersons(filter, firstRow,
LazyList.DEFAULT_PAGE_SIZE);
}
};
final LazyComboBox.FilterableCountProvider filterableCountProvider = new
LazyComboBox.FilterableCountProvider() {
@Override
public int size(String filter) {
return service.countPersons(filter);
}
};
@Override
public String getCaption(Person option) {
return option.getFirstName() + " " + option.
getLastName();
}
});
And the lambda version of the above:
final LazyComboBox<Person> cb = new LazyComboBox(Person.class,
(firstRow, filter) -> return service.findPersons(filter, firstRow,
LazyList.DEFAULT_PAGE_SIZE),
filter -> return service.countPersons(filter)
).setCaptionGenerator(p -> return p.getFirstName() + " " + p.getLastName());
12
Non-lowercase filter string
LazyComboBox by default (like core Vaadin always) lowercases the filter string. This can
be avoided by calling setUseRawFilter(true).
Eager Validation
Clone in Desktop
By eager validation we mean that user input is validated while the date is being inputted
and NOT postponed until the user presses Submit or Save. In eager validation you also
typically control your save button's enabled state, based on the validity of your input
fields. This highly improves UX and puts your web app's forms in par with typical desktop
UI's. This slightly increases the chattiness of the application, but in most cases this is irrele-
vant if you really start to investigate your apps performance characteristics.
Eager validation is used by default by AbstractForm and is also supported the MBeanField-
Group class. Also MTextField has a special support for eager validation, while user is typ-
ing into the text field, so it is highly suggested to use that instead of raw TextField in your
form.
AbstractForm
Clone in Desktop
AbstractForm tries to make editing simple Java beans as easy as possible. It also tries to
guide you towards good UX for the forms you build to edit your beans. It for example by
default uses eager validation, so that users cannot click save until they have filled in a
proper input. This also makes your job a bit easier.
Like the core Vaadin, AbstractForm supports JSR-303 style validation (as well as other pro-
grammatic validation). In the example in this page, we use a following bean:
public class Person {
private int id;
@NotNull
@Size(min = 3, max = 15)
private String firstName;
private String lastName;
@NotNull
private Integer age;
// getters/setters omitted
}
To create a re-usable form for that bean you could use following class:
public static class PersonForm extends AbstractForm<Person> {
13
private MTextField firstName = new MTextField("Name");
private IntegerField age = new IntegerField("Age");
@Override
protected Component createContent() {
return new MVerticalLayout(firstName, age
getToolbar());
}
}
The above class only allows user to edit firstName and age properties. The getToolbar()
method, which you can override (see implementation), contains basic Save, Cancel and
Delete buttons, which appear if related handlers have been set.
Handlers
You can define handlers inside your form class or separately, which ever suits better for
your architecture. If you are using the default getToolbar(), setting handlers also make a re-
lated button appear in the UI. Save button is only active (by default, see protected void ad-
justSaveButtonState() method), if the form has unsaved changes and, if the form uses ea-
ger validation, the changes pass all validation.
All handlers are functional interfaces with only one parameter, the currently edited bean.
NOTE: You are by no means tied to using these handlers, you can also just add your own
save/cancel features or tie it to automatic saving.
TIP: If you want to use the built in handlers and buttons, but don't like how buttons are
enabled/disabled by default, override methods adjustSave(Reset/Deleted)ButtonState.
SaveHandler
An example usage for SaveHandler (as lambda expression):
form.setSavedHandler(entity -> service.save(entity));
ResetHandler
An example usage for ResetHandler (as method reference):
form.setResetHandler(this::closeEditor);
DeleteHandler
An example usage for DeleteHandler (as lambda expression):
form.setDeleteHandler(entity -> service.remove(entity));
14
Helper for "popup" usage
In apps that target desktop users, it is often handy to show the form in a (modal) popup.
The form has a built in helper method openInModalPopup() for this use case. It also auto-
matically focuses the first field inside the popup for better default UX.
Opening with the helper method:
form.openInModalPopup();
There is also helper to return the popup (if currenly open) and to close it:
Window popup = form.getPopup();
// or you can use shorthand to close it, e.g. in reset handler
form.closePopup();
public PersonForm() {
setNestedProperties("address.street","address.city", "address.zipCode",
"address.type");
}
// ....
Cross-field validation
AbstractForm supports Cross-Field-Validation. See the referred page for more details.
15
If you use bean level validators, you should also decide how to show possible validation
errors for them. Abstract field contains a method called Component getConstraintViola-
tionsDisplay() that returns a built in display for bean level validation errors. Easiest way is
to use that and show that somewhere in your UI. If you want to handle them manually,
MBeanFieldGroup contains a method public Collection getBeanLevelValidationErrors() that
you can use to retrieve current bean level validation constraint messages.
Alternatively you can target bean level validation errors to be displayed on certain fields.
See sub topics for more details.
MValidator interface
MValidator is a well typed sister for the Validator in the Vaadin core. The parameter the vali-
date method gets is of the type of bean that is edited, so it can be used for bean level (or
"cross-field") validation as well. Those can be added to AbstractForm or MBeanField-
Group using addValidator method.
An example usage for form editing Reservation beans:
addValidator(reservation -> {
if (reservation.getStart().getTime() > reservation.getEnd().
getTime()) {
throw new Validator.InvalidValueException(
"End time cannot be before start time!");
}
});
Optionally you can specify which Fields are related to this validator. If you specify the
fields, the error message is shown on those fields.
16