Class FieldBuilder<T>

Type Parameters:
T - backing object type
All Implemented Interfaces:
Serializable

public class FieldBuilder<T> extends AbstractGridFieldBuilder<FieldBuilder<T>,T>
Automatically configures and binds fields using declarative method annotations.

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.

Recursive Validation

To facilitate nesting/recursion of fields, fields that implement ValidatingField will be automatically registered as a field Validator by bindFields() when the field is bound. This allows for more modularity with respect to validation when nested types having sub-fields are in use. See also FieldBuilderCustomField, which 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:
  • Constructor Details

    • FieldBuilder

      public FieldBuilder(Class<T> type)
      Constructor.
      Parameters:
      type - backing object type
    • FieldBuilder

      public FieldBuilder(FieldBuilder<T> original)
      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 - if original is null