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>
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:
-
Nested Class Summary
Nested ClassesNested classes/interfaces inherited from class com.vaadin.flow.component.AbstractField
AbstractField.ComponentValueChangeEvent<C extends Component,V extends Object> Nested classes/interfaces inherited from interface com.vaadin.flow.component.BlurNotifier
BlurNotifier.BlurEvent<C extends Component>Nested classes/interfaces inherited from interface com.vaadin.flow.component.FocusNotifier
FocusNotifier.FocusEvent<C extends Component>Nested classes/interfaces inherited from interface com.vaadin.flow.component.HasValue
HasValue.ValueChangeEvent<V extends Object>, HasValue.ValueChangeListener<E extends HasValue.ValueChangeEvent<?>> -
Field Summary
FieldsModifier and TypeFieldDescriptionprotected final FieldBuilderCustomField.DialogFormprotected AbstractFieldBuilder<?,T> The field builder that builds this instance's sub-fields.Fields inherited from class org.dellroad.stuff.vaadin24.field.BinderCustomField
binder, modelType, subfieldValidationErrors -
Constructor Summary
ConstructorsConstructorDescriptionFieldBuilderCustomField(Class<T> modelType) Constructor.FieldBuilderCustomField(Class<T> modelType, FieldBuilderCustomField.DialogForm dialogForm) Constructor.Auto-configure Constructor. -
Method Summary
Modifier and TypeMethodDescriptionprotected voidCreate this field's sub-fields and bind them toBinderCustomField.binder.protected AbstractFieldBuilder<?,T> Create a newAbstractFieldBuilderfor the given type.protected voidprotected AbstractField<?,?> Get theFieldComponentsub-field corresponding to the given field name.protected FieldComponent<?>getFieldComponent(String name) Get theAbstractFieldsub-field corresponding to the given field name.protected voidLayout the components required for this field.protected voidlayoutEditDialogFields(FormLayout formLayout) Layout components required for this field in a separate edit dialog window.protected voidLayout components required for this field when a separate edit dialog window is not being used.protected voidLayout components required for this field when a separate edit dialog window is being used.protected voidopenEditDialog(T bean) Open a dialog window containing a form for editing the given value.protected booleansubmitEditDialog(T bean) Methods inherited from class org.dellroad.stuff.vaadin24.field.BinderCustomField
createBinder, createNewBean, generateModelValue, getModelType, initialize, setEnabled, setErrorMessage, setPresentationValue, validateMethods inherited from class com.vaadin.flow.component.customfield.CustomField
add, addThemeVariants, getLabel, onAttach, remove, removeThemeVariants, setLabel, updateValueMethods inherited from class com.vaadin.flow.component.AbstractField
addValueChangeListener, getEmptyValue, getValue, isEmpty, setModelValue, setValue, valueEqualsMethods inherited from class com.vaadin.flow.component.Component
addListener, findAncestor, fireEvent, from, get, getChildren, getElement, getEventBus, getId, getListeners, getLocale, getParent, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getUI, hasListener, isAttached, isTemplateMapped, isVisible, onDetach, onEnabledStateChanged, removeFromParent, scrollIntoView, scrollIntoView, set, setElement, setId, setVisibleMethods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface com.vaadin.flow.component.AttachNotifier
addAttachListenerMethods inherited from interface com.vaadin.flow.component.BlurNotifier
addBlurListenerMethods inherited from interface com.vaadin.flow.component.DetachNotifier
addDetachListenerMethods inherited from interface com.vaadin.flow.component.Focusable
addFocusShortcut, blur, focus, getTabIndex, setTabIndexMethods inherited from interface com.vaadin.flow.component.FocusNotifier
addFocusListenerMethods inherited from interface com.vaadin.flow.component.HasElement
getElementMethods inherited from interface com.vaadin.flow.component.HasEnabled
isEnabledMethods inherited from interface com.vaadin.flow.component.HasHelper
getHelperComponent, getHelperText, setHelperComponent, setHelperTextMethods inherited from interface com.vaadin.flow.component.HasSize
getHeight, getHeightUnit, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getWidth, getWidthUnit, setHeight, setHeight, setHeightFull, setMaxHeight, setMaxHeight, setMaxWidth, setMaxWidth, setMinHeight, setMinHeight, setMinWidth, setMinWidth, setSizeFull, setSizeUndefined, setWidth, setWidth, setWidthFullMethods inherited from interface com.vaadin.flow.component.HasStyle
addClassName, addClassNames, getClassName, getClassNames, getStyle, hasClassName, removeClassName, removeClassNames, setClassName, setClassNameMethods inherited from interface com.vaadin.flow.component.HasTheme
addThemeName, addThemeNames, getThemeName, getThemeNames, hasThemeName, removeThemeName, removeThemeNames, setThemeName, setThemeNameMethods inherited from interface com.vaadin.flow.component.shared.HasTooltip
getTooltip, setTooltipTextMethods inherited from interface com.vaadin.flow.component.HasValidation
setManualValidationMethods inherited from interface com.vaadin.flow.component.shared.HasValidationProperties
getErrorMessage, isInvalid, setInvalidMethods inherited from interface com.vaadin.flow.component.HasValue
addValueChangeListener, clear, getEmptyValue, getOptionalValue, getValue, isEmpty, setValueMethods inherited from interface com.vaadin.flow.component.HasValueAndElement
isReadOnly, isRequiredIndicatorVisible, setReadOnly, setRequiredIndicatorVisibleMethods inherited from interface org.dellroad.stuff.vaadin24.field.ValidatingField
addValidationTo
-
Field Details
-
dialogForm
-
fieldBuilder
The field builder that builds this instance's sub-fields.
-
-
Constructor Details
-
FieldBuilderCustomField
Auto-configure Constructor.This constructor will infer the target type and find any
@FieldBuilderCustomField.DialogFormannotation by inspecting the annotated method.- Parameters:
ctx- field builder context- Throws:
NullPointerException- ifctxis null
-
FieldBuilderCustomField
Constructor.- Parameters:
modelType- model type to introspect forAbstractFieldBuilderannotations- Throws:
IllegalArgumentException- ifmodelTypeis null
-
FieldBuilderCustomField
Constructor.- Parameters:
modelType- model type to introspect forAbstractFieldBuilderannotationsdialogForm- configuration for using aDialogwindow to edit the field value, or null for inline editing- Throws:
IllegalArgumentException- ifmodelTypeis null
-
-
Method Details
-
createAndBindFields
protected void createAndBindFields()Create this field's sub-fields and bind them toBinderCustomField.binder.The implementation in
FieldBuilderCustomFieldjust invokesFieldBuilder.bindFields(this.binder).- Specified by:
createAndBindFieldsin classBinderCustomField<T>
-
createFieldBuilder
Create a newAbstractFieldBuilderfor the given type.The implementation in
FieldBuilderCustomFieldreturns a newFieldBuildereach time.- Returns:
- field builder
-
layoutComponents
protected void layoutComponents()Layout the components required for this field.The implementation in
FieldBuilderCustomFielddelegates tolayoutInlineComponents()if#dialogFormis null, otherwiselayoutNonInlineComponents().- Overrides:
layoutComponentsin classBinderCustomField<T>
-
layoutInlineComponents
protected void layoutInlineComponents()Layout components required for this field when a separate edit dialog window is not being used.The implementation in
FieldBuilderCustomFielditerates the bound fields infieldBuilderinto a newHorizontalLayoutwhich is then added to this instance. -
layoutNonInlineComponents
protected void layoutNonInlineComponents()Layout components required for this field when a separate edit dialog window is being used.The implementation in
FieldBuilderCustomField -
editButtonPressed
protected void editButtonPressed() -
openEditDialog
Open a dialog window containing a form for editing the given value.- Parameters:
bean- field value to edit- Throws:
IllegalArgumentException- ifbeanis nullIllegalStateException- ifthis.dialogFormis null
-
submitEditDialog
-
layoutEditDialogFields
Layout components required for this field in a separate edit dialog window.The implementation in
FieldBuilderCustomFielddelegtes toAbstractFieldBuilder.addFieldComponents(com.vaadin.flow.component.formlayout.FormLayout).- Parameters:
formLayout- form
-
getField
Get theFieldComponentsub-field corresponding to the given field name.- Parameters:
name- field name- Returns:
- corresponding
FieldComponent - Throws:
IllegalArgumentException- ifnameis not foundIllegalArgumentException- ifnameis null
-
getFieldComponent
Get theAbstractFieldsub-field corresponding to the given field name.- Parameters:
name- field name- Returns:
- corresponding
AbstractField - Throws:
IllegalArgumentException- ifnameis not foundIllegalArgumentException- ifname's field is not anAbstractFieldIllegalArgumentException- ifnameis null
-