Class FieldBuilder<T>
- Type Parameters:
T
- backing object type
- All Implemented Interfaces:
Serializable
FieldBuilder
annotations allow for the automatic construction and configuration of fields for editing a bean.
Annotations on "getter" methods specify how the fields that edit the corresponding bean property should be constructed,
configured, and bound to a Binder
. This allows all information about how to edit a Java type
to be specified declaratively. Annotations are also provided to configure how to add the generated fields to a
FormLayout
.
The primary method is bindFields()
, which automatically creates, configures, and binds fields
into a given Binder
based on information gleaned from scanned annotations.
@FieldBuilder.Foo
vs. @ProvidesField
FieldBuilder
supports two types of annotations from which it infers how to construct and configure a field
for editing an associated bean property.
The various @FieldBuilder.Foo
annotations are the purely declarative way to specify how to construct a field.
Each annotation corresponds to a specific field class (e.g., @FieldBuilder.TextField
configures a TextField
). The annotation's properties parallel the
properties of the field and specify how to construct, configure, and bind an instance of the field.
The annotation annotates the bean property's "getter" method.
@ProvidesField
provides a more general approach, but it requires writing code.
Use @ProvidesField
on a method that itself knows how to build a field suitable
for editing the named property. The method should return a component for editing the property. Both instance and static
methods are supported; instance methods require that the Binder
being used has a bound bean.
In all cases, an annotation on a subclass method will override the same annotation on the corresponding superclass method.
Fields defined from these annotations are created, configured, and bound via bindFields()
.
Configuring the Binding
In addition to constructing and configuring the fields associated with each bean property into the Binder
,
you may also want to configure the bindings themselves, for example, to specify a Converter
or Validator
.
The @FieldBuilder.Binding
annotation allows you to configure the
binding using properties corresponding to methods in Binder.BindingBuilder
.
Adding Fields to a FormLayout
The @FieldBuilder.FormLayout
annotation allows you to configure field labels,
column span, and ordering of fields in a FormLayout
.
Fields are added to a FormLayout
via
addFieldComponents()
.
Example
A simple example shows how these annotations are used:
@FieldBuilder.TextField(placeholder = "Enter your name...", maxLength = 64)
@FieldBuilder.Binding(required = "Name is mandatory", validators = MyValidator.class)
@FieldBuilder.FormLayout(label = "Name:", colspan = 2, order = 1)
@NotNull
public String getName() { ... }
@FieldBuilder.ComboBox(items = EnumDataProvider.class)
@FieldBuilder.Binding(required = "Status is mandatory")
@FieldBuilder.FormLayout(label = "Status:", order = 2)
@NotNull
public Status getStatus() { ... }
// A property that can't be edited with existing fields
public Foobar getFoobar() { ... }
// Instead, use my own custom field to edit "foobar"
@FieldBuilder.ProvidesField("foobar")
@FieldBuilder.FormLayout(label = "Your Foobar:", order = 3)
private static CustomField<Foobar> createFoobarField() { ... }
All of the declarative @FieldBuilder.Foo
annotations have an implementation()
property that allows you
to specify a custom implementation. So a more consistent way to customize the "foobar"
component would be:
// Use my own custom FoobarField to edit "foobar"
@FieldBuilder.CustomField(label = "Your Foobar:", implementation = FoobarField.class)
public Foobar getFoobar() { ... }
Building the Form
First, use bindFields()
to create a new set of fields, configure them, and bind them into a Binder
:
// Create a FieldBuilder
FieldBuilder<Person> fieldBuilder = new FieldBuilder<>(Person.class);
// Create a Binder and bind fields
Binder<Person> binder = new Binder<>(Person.class);
fieldBuilder.bindFields(binder);
Then (optionally) use addFieldComponents()
to add and configure those fields into a
FormLayout
:
// Create form and add fields to it
FormLayout form = new FormLayout();
fieldBuilder.addFieldComponents(form);
You can also access the fields directly via getFieldComponents()
.
Efficient Reuse
A FieldBuilder
can be used multiple times. Each time bindFields()
is invoked any previously
created fields are forgotten and a new set of fields is created and bound. This avoids the relatively expensive process
of scanning the class hierarchy for annotations that occurs during construction.
FieldBuilder
also has a copy constructor, which accomplishes the same thing.
Alternate Defaults
You can override the default value for specific field properties on a per-property-name basis by annotating static methods
in the edited model class with @FieldBuilder.FieldDefault
.
For example:
public class Person {
public String getFirstName() { ... }
public String getLastName() { ... }
@FieldBuilder.FieldDefault("itemLabelGenerator")
private static ItemLabelGenerator<Person> buildPersonILG() {
return person -> person.getLastName() + ", " + person.getFirstName();
}
}
These defaults can be accessed via getScannedFieldDefaults()
.
See @FieldBuilder.FieldDefault
for details.
Providing Context via FieldBuilderContext
All classes instantiated by FieldBuilder
(fields, data providers, converters, validatiors, etc.) are instantiated
using the default constructor, unless a constructor taking a FieldBuilderContext
exists, in which case it will
be used instead. In this latter case, the new object can do further introspection of the method and/or annotation
for the purpose of doing additional automated self-configuration.
For example, a @FieldBuilder.ComboBox
annotation might specify a general purpose
DataProvider
implementation that determines which object type to provide from
the method's return type. This is how EnumDataProvider
automatically infers the Enum
type to use.
Override newFieldBuilderContext()
if you wish to pass a custom context; the constructor
chosen will be the one with the narrowest type compatible with the actual FieldBuilderContext
in use.
Automatic Validator Registration
This class automatically registers validators with the Binder
as follows:
- If a field implements
ValidatingField
, a corresponding field validator will be registered - If the target bean class implements
ValidatingBean
, a corresponding bean-level validator will be registered
This allows for more modularity, especially when nested types having sub-fields are in use; for example,
FieldBuilderCustomField
relies on this mechanism.
Production Bundle Caveat
If you build a Vaadin production bundle then you may run into an issue where a field does not appear and Vaadin logs a warning like this:
The component class com.vaadin.flow.component.datepicker.DatePicker includes '@vaadin/date-picker/src/vaadin-date-picker.js' but this file was not included when creating the production bundle. The component will not work properly. Check that you have a reference to the component and that you are not using it only through reflection. If needed add a @Uses(DatePicker.class) where it is used.This happens because Vaadin thinks your application never uses
FieldBuilder.DatePicker
because it doesn't see your code
directly instantiating one anywhere. In other words, the indirect instantiation by this class goes undetected.
To fix this, either add the @Uses
annotation to one of your view classes as described in the warning,
or else configure your Vaadin Maven plugin with <optimizeBundle>false</optimizeBundle>
.
Homebrew Your Own
You can create your own version of this class containing auto-generated annotations for whatever classes you want
simply by subclassing AbstractFieldBuilder
and applying a Maven plugin. See source code for details.
- See Also:
-
FieldBuilder.Checkbox
FieldBuilder.CheckboxGroup
FieldBuilder.ComboBox
FieldBuilder.MultiSelectComboBox
FieldBuilder.CustomField
FieldBuilder.DatePicker
FieldBuilder.DateTimePicker
FieldBuilder.Input
FieldBuilder.RangeInput
FieldBuilder.ListBox
FieldBuilder.MultiSelectListBox
FieldBuilder.RadioButtonGroup
FieldBuilder.Select
FieldBuilder.BigDecimalField
FieldBuilder.EmailField
FieldBuilder.IntegerField
FieldBuilder.NumberField
FieldBuilder.PasswordField
FieldBuilder.TextArea
FieldBuilder.TextField
FieldBuilder.TimePicker
- Serialized Form
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic @interface
Specifies how a Java bean property should be edited using aBigDecimalField
.static @interface
Specifies how a Java bean property should be edited using aCheckbox
.static @interface
Specifies how a Java bean property should be edited using aCheckboxGroup
.static @interface
Specifies how a Java bean property should be edited using aComboBox
.static @interface
Specifies how a Java bean property should be edited using aCustomField
.static @interface
Specifies how a Java bean property should be edited using aDatePicker
.static @interface
Specifies how a Java bean property should be edited using aDateTimePicker
.static @interface
Specifies how a Java bean property should be edited using anEmailField
.static @interface
Specifies how a Java bean property should be edited using anInput
.static @interface
Specifies how a Java bean property should be edited using anIntegerField
.static @interface
Specifies how a Java bean property should be edited using aListBox
.static @interface
Specifies how a Java bean property should be edited using aMultiSelectComboBox
.static @interface
Specifies how a Java bean property should be edited using aMultiSelectListBox
.static @interface
Specifies how a Java bean property should be edited using aNumberField
.static @interface
Specifies how a Java bean property should be edited using aPasswordField
.static @interface
Specifies how a Java bean property should be edited using aRadioButtonGroup
.static @interface
Specifies how a Java bean property should be edited using aRangeInput
.static @interface
Specifies how a Java bean property should be edited using aSelect
.static @interface
Specifies how a Java bean property should be edited using aTextArea
.static @interface
Specifies how a Java bean property should be edited using aTextField
.static @interface
Specifies how a Java bean property should be edited using aTimePicker
.Nested classes/interfaces inherited from class org.dellroad.stuff.vaadin24.field.AbstractGridFieldBuilder
AbstractGridFieldBuilder.GridMultiSelect, AbstractGridFieldBuilder.GridSingleSelect
Nested classes/interfaces inherited from class org.dellroad.stuff.vaadin24.field.AbstractFieldBuilder
AbstractFieldBuilder.Binding, AbstractFieldBuilder.BindingInfo, AbstractFieldBuilder.DefaultInfo, AbstractFieldBuilder.EnabledBy, AbstractFieldBuilder.FieldDefault, AbstractFieldBuilder.FormLayout, AbstractFieldBuilder.NullifyCheckbox, AbstractFieldBuilder.ProvidesField
-
Field Summary
Fields inherited from class org.dellroad.stuff.vaadin24.field.AbstractFieldBuilder
DEFAULT_ANNOTATION_DEFAULTS_METHOD_NAME, DEFAULT_IMPLEMENTATION_PROPERTY_NAME
-
Constructor Summary
ConstructorDescriptionFieldBuilder
(Class<T> type) Constructor.FieldBuilder
(FieldBuilder<T> original) Static information copy constructor. -
Method Summary
Methods inherited from class org.dellroad.stuff.vaadin24.field.AbstractGridFieldBuilder
buildDeclarativeField, buildGrid, getDeclarativeAnnotationTypes
Methods inherited from class org.dellroad.stuff.vaadin24.field.AbstractFieldBuilder
addFieldComponents, applyFieldDefaultAnnotations, bindFields, buildProvidedField, createBindingInfo, getAnnotationDefaultsMethod, getAnnotationDefaultsMethodName, getDefaultsFor, getDefaultsFor, getFieldComponents, getImplementationPropertyName, getScannedFieldDefaults, getScannedProperties, getType, instantiate, instantiate, newFieldBuilderContext, scanForAnnotations, scanForFieldDefaultAnnotations
-
Constructor Details
-
FieldBuilder
Constructor.- Parameters:
type
- backing object type
-
FieldBuilder
Static information copy constructor.Using this constructor is more efficient than repeatedly scanning the same classes for the same annotations.
Only the static information gathered by this instance by scanning for annotations is copied. Any previously bound fields are not copied.
- Parameters:
original
- original instance- Throws:
IllegalArgumentException
- iforiginal
is null
-