Class FieldBuilderCustomField<T>

Type Parameters:
T - field value type
All Implemented Interfaces:
AttachNotifier, BlurNotifier<CustomField<T>>, DetachNotifier, Focusable<CustomField<T>>, FocusNotifier<CustomField<T>>, HasElement, HasEnabled, HasHelper, HasLabel, HasSize, HasStyle, HasTheme, HasValidation, HasValue<AbstractField.ComponentValueChangeEvent<CustomField<T>,T>,T>, HasValueAndElement<AbstractField.ComponentValueChangeEvent<CustomField<T>,T>,T>, HasTooltip, HasValidationProperties, InputField<AbstractField.ComponentValueChangeEvent<CustomField<T>,T>,T>, Serializable, ValidatingField<AbstractField.ComponentValueChangeEvent<CustomField<T>,T>,T>

public class FieldBuilderCustomField<T> extends BinderCustomField<T>
Support superclass that mostly automates the creation of CustomFields for editing any model type using sub-fields automatically generated from FieldBuilder annotations to arbitrary recursion depth.

This class is just a BinderCustomField that uses a FieldBuilder to generate the sub-fields.

Example

Suppose model class Contract has a "term" property of type DateInterval which has a start and end date. We want to use FieldBuilder annotations to define the editor fields for the properties of Contract, including "term", but for that to work we will need to specify a custom field to handle the "term" property. We also want this custom field to contain the logic for laying out the two date picker components as well as for validating proper ordering.

For example:


 // This is complex type that we wish to edit with a single CustomField
 public class DateInterval {

     @FieldBuilder.DatePicker(label = "Start date")
     public LocalDate getStartDate() { ... }
     public void setStartDate(LocalDate date) { ... }

     @FieldBuilder.DatePicker(label = "End date")
     public LocalDate getEndDate() { ... }
     public void setEndDate(LocalDate date) { ... }
 }

 // This is the corresponding custom field
 public class DateIntervalField extends FieldBuilderCustomField<DateInterval> {

     public DateIntervalField() {
         super(DateInterval.class);
     }

     // Customize how we want to layout the subfields
     @Override
     protected void layoutComponents() {
        this.add(new HorizontalLayout(
          new Text("From"), this.getField("startDate")), new Text("to"), this.getField("endDate"));
     }

     // Bean level validation: ensure end date is after start date
     @Override
     public ValidationResult validate(DateInterval dates, ValueContext ctx) {
         if (dates.getStartDate().isAfter(dates.getEndDate())
             return ValidationResult.error("Dates out-of-order"));
         return super.validate(dates, ctx);           // always ensure superclass contraints apply also
     }
 }
 

Once that's done, using FieldBuilder works recursively and automatically for this multi-level class:


 public class Contract {

     @FieldBuilder.CheckBox(label = "Approved?")
     public boolean isApproved() { ... }
     public void setApproved(boolean approved) { ... }

     @FieldBuilder.CustomField(label = "Term", implementation = DateIntervalField.class)
     public DateInterval getTerm() { ... }
     public void setTerm(DateInterval term) { ... }
 }
 

If you don't need to do any customization, you can use FieldBuilderCustomField directly; the field's value type will be inferred by the FieldBuilderCustomField(FieldBuilderContext) constructor:


 public class Contract {

     @FieldBuilder.CheckBox(label = "Approved?")
     public boolean isApproved() { ... }
     public void setApproved(boolean approved) { ... }

     // "DateInterval" value type is inferred from the method's return type
     @FieldBuilder.CustomField(label = "Term", implementation = FieldBuilderCustomField.class)
     public DateInterval getTerm() { ... }
     public void setTerm(DateInterval term) { ... }
 }
 

Edit in Dialog Window

For more complex value types, this class support an alternative display model in which the value's sub-fields are edited in a Dialog window instead of inline in the form. In lieu of the inline sub-fields, the form displays an "Edit" button which, when clicked, opens a new Dialog window into which the sub-fields are laid out, plus "OK" and "Cancel" buttons.

To configure an edit dialog, add a @FieldBuilderCustomField.DialogForm annotation to the method:


 public class Contract {

     @FieldBuilder.CheckBox(label = "Approved?")
     public boolean isApproved() { ... }
     public void setApproved(boolean approved) { ... }

     @FieldBuilder.CustomField(label = "Term", implementation = FieldBuilderCustomField.class)
     @FieldBuilderCustomField.DialogForm(windowTitle = "Contract Term")
     public DateInterval getTerm() { ... }
     public void setTerm(DateInterval term) { ... }
 }
 

Nullable Values

When the field's value can be null, consider adding a @FieldBuilder.NullifyCheckbox annotation.

See Also: