Vaadin Platform (vaadin-core) - Core component of the Vaadin web framework platform
Data binding and providers for connecting UI components to backend data sources.
Quick links: Binder | DataProvider | Hierarchical DataProvider | Renderers
Related components: Grid (see Components), ComboBox (see Components)
Two-way data binding between UI components and Java beans.
class Binder<BEAN> {
Binder();
Binder(Class<BEAN> beanType);
<FIELDVALUE> BindingBuilder<BEAN, FIELDVALUE> forField(HasValue<?, FIELDVALUE> field);
void setBean(BEAN bean);
void readBean(BEAN bean);
boolean writeBeanIfValid(BEAN bean) throws ValidationException;
BinderValidationStatus<BEAN> validate();
}
interface BindingBuilder<BEAN, FIELDVALUE> {
BindingBuilder<BEAN, FIELDVALUE> withValidator(Validator<? super FIELDVALUE> validator);
BindingBuilder<BEAN, FIELDVALUE> asRequired(String errorMessage);
<NEWTARGET> BindingBuilder<BEAN, NEWTARGET> withConverter(Converter<FIELDVALUE, NEWTARGET> converter);
Binding<BEAN, FIELDVALUE> bind(ValueProvider<BEAN, FIELDVALUE> getter,
Setter<BEAN, FIELDVALUE> setter);
Binding<BEAN, FIELDVALUE> bind(String propertyName);
}
interface Validator<T> {
ValidationResult apply(T value, ValueContext context);
}Examples:
Binder<Person> binder = new Binder<>(Person.class);
TextField firstName = new TextField("First Name");
binder.forField(firstName)
.asRequired("Required")
.withValidator(name -> name.length() >= 2, "Min 2 characters")
.bind(Person::getFirstName, Person::setFirstName);
EmailField email = new EmailField("Email");
binder.forField(email)
.asRequired("Required")
.bind(Person::getEmail, Person::setEmail);
// Load data
binder.setBean(person);
// Save data
if (binder.writeBeanIfValid(person)) {
personService.save(person);
Notification.show("Saved");
}Interface for providing data to components like Grid and ComboBox.
interface DataProvider<T, F> {
Stream<T> fetch(Query<T, F> query);
int size(Query<T, F> query);
void refreshItem(T item);
void refreshAll();
static <T> ListDataProvider<T> ofCollection(Collection<T> items);
static <T, F> DataProvider<T, F> fromCallbacks(
CallbackDataProvider.FetchCallback<T, F> fetchCallback,
CallbackDataProvider.CountCallback<T, F> countCallback);
}
class ListDataProvider<T> implements DataProvider<T, SerializablePredicate<T>> {
ListDataProvider(Collection<T> items);
void setFilter(SerializablePredicate<T> filter);
void setSortOrder(ValueProvider<T, ?> valueProvider, SortDirection direction);
}
class Query<T, F> {
int getOffset();
int getLimit();
List<QuerySortOrder> getSortOrders();
Optional<F> getFilter();
}
abstract class AbstractBackEndDataProvider<T, F> implements DataProvider<T, F> {
protected abstract Stream<T> fetchFromBackEnd(Query<T, F> query);
protected abstract int sizeInBackEnd(Query<T, F> query);
}Examples:
// Grid with DataProvider
Grid<Customer> grid = new Grid<>(Customer.class);
grid.setColumns("name", "email");
DataProvider<Customer, Void> dataProvider = DataProvider.fromCallbacks(
query -> {
int offset = query.getOffset();
int limit = query.getLimit();
List<QuerySortOrder> sortOrders = query.getSortOrders();
return customerService.fetch(offset, limit, sortOrders).stream();
},
query -> customerService.count()
);
grid.setDataProvider(dataProvider);
// ListDataProvider with filtering
ListDataProvider<Product> listProvider = DataProvider.ofCollection(products);
TextField searchField = new TextField();
searchField.addValueChangeListener(e -> {
String term = e.getValue().toLowerCase();
listProvider.setFilter(p -> p.getName().toLowerCase().contains(term));
});
grid.setDataProvider(listProvider);For tree structures like TreeGrid.
interface HierarchicalDataProvider<T, F> extends DataProvider<T, F> {
int getChildCount(HierarchicalQuery<T, F> query);
boolean hasChildren(T item);
}
abstract class AbstractBackEndHierarchicalDataProvider<T, F>
implements HierarchicalDataProvider<T, F> {
protected abstract Stream<T> fetchChildrenFromBackEnd(HierarchicalQuery<T, F> query);
public abstract int getChildCount(HierarchicalQuery<T, F> query);
public abstract boolean hasChildren(T item);
}Example:
TreeGrid<Department> treeGrid = new TreeGrid<>();
treeGrid.addHierarchyColumn(Department::getName);
HierarchicalDataProvider<Department, Void> provider =
new AbstractBackEndHierarchicalDataProvider<Department, Void>() {
@Override
protected Stream<Department> fetchChildrenFromBackEnd(
HierarchicalQuery<Department, Void> query) {
Department parent = query.getParent();
return parent == null ?
departmentService.fetchRoots(query.getOffset(), query.getLimit()).stream() :
departmentService.fetchChildren(parent, query.getOffset(), query.getLimit()).stream();
}
@Override
public int getChildCount(HierarchicalQuery<Department, Void> query) {
Department parent = query.getParent();
return parent == null ?
departmentService.countRoots() :
departmentService.countChildren(parent);
}
@Override
public boolean hasChildren(Department item) {
return departmentService.hasChildren(item);
}
};
treeGrid.setDataProvider(provider);class ComponentRenderer<C extends Component, T> implements Renderer<T> {
ComponentRenderer(SerializableFunction<T, C> componentProvider);
ComponentRenderer(SerializableFunction<T, C> componentProvider,
SerializableConsumer<C> componentUpdater);
}
class LitRenderer<T> implements Renderer<T> {
static <T> LitRenderer<T> of(String template);
LitRenderer<T> withProperty(String property, ValueProvider<T, ?> provider);
}Examples:
Grid<Product> grid = new Grid<>();
grid.addColumn(new ComponentRenderer<>(product ->
new HorizontalLayout(
new Image(product.getImageUrl(), ""),
new Span(product.getName())
)
));
grid.addColumn(LitRenderer.<Product>of("<b>[[item.name]]</b><br><small>[[item.description]]</small>")
.withProperty("name", Product::getName)
.withProperty("description", Product::getDescription));Install with Tessl CLI
npx tessl i tessl/maven-com-vaadin--vaadin-core