Class EasyForm<T>
- Type Parameters:
T- the bean type
- All Implemented Interfaces:
AttachNotifier,DetachNotifier,HasElement,HasEnabled,HasSize,HasStyle,Serializable
EasyForm introspects the properties of the given bean type (via getter/setter
conventions) and creates an appropriate Vaadin form field for each one, configures validations
based on JSR-380 (Bean Validation) annotations, and manages data binding through an internal
Binder. Properties without both a getter and a setter, or whose type has no registered
component factory, are ignored.
All customization is programmatic through a fluent API:
EasyForm<Person> form = new EasyForm<>(Person.class);
form.configureField("email").withLabel("Email Address").asRequired("Email is required");
form.setSaveAction(person -> personService.save(person));
add(form);
Discovery is flat: only the direct properties of the bean type are used. Nested property paths
such as "address.street", which Binder itself supports, are not discovered, and
asking for one through configureField(String) fails as for any unknown property.
Subclasses can influence generation by overriding includeProperty(PropertyDescriptor),
createComponent(String, Class), createLabel(String) and
configureComponent(String, HasValue). These are called while the constructor discovers
the properties, so an override must not depend on state initialized in the subclass constructor
body.
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic classThe texts of the button bar, following the Vaadin i18n convention.static final classFluent configuration wrapper for a single field of anEasyForm. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionaddBeanValidator(Validator<? super T> validator) Adds a bean-level (cross-field) validator.addButton(String text, ComponentEventListener<ClickEvent<Button>> clickListener, ButtonVariant... variants) Adds an extra button to the button bar.addButton(String text, Component icon, ComponentEventListener<ClickEvent<Button>> clickListener, ButtonVariant... variants) Adds an extra button with an icon to the button bar.addStatusChangeListener(StatusChangeListener listener) Adds a listener notified whenever the validation status of the form changes, which is the supported way to keep the surrounding UI ? a save button, a summary ? in step with the form.addValueChangeListener(HasValue.ValueChangeListener<? super HasValue.ValueChangeEvent<?>> listener) Adds a listener notified whenever the value of any bound field changes, whether or not the new value is valid.clear()Clears all fields and detaches the current bean, so that the cleared values are never written to it: after this callgetBean()returnsnullandgetValidBean()writes to a new instance.protected voidconfigureComponent(String propertyName, HasValue<?, ?> component) Called after a generated component has been created and its label, placeholder and helper text applied, for decoration that applies to every field ? style names, widths, theme variants.configureField(String propertyName) Returns the configuration wrapper for the field generated for the given property.<V> EasyForm.Field<V>configureField(String propertyName, Class<V> valueType) Returns the configuration wrapper for the field generated for the given property, typed to the given presentation value type.protected HasValue<?,?> createComponent(String propertyName, Class<?> propertyType) Creates the component for a property that has no registered component factory.protected StringcreateLabel(String propertyName) Returns the label for a property whose label was not set explicitly throughEasyForm.Field.withLabel(String).HasValue<?,?> Returns the component generated for the given property, which is whatconfigureField(String)wraps.<V> HasValue<?,V> Returns the component generated for the given property, typed to the given presentation value type:Returns the configuration wrapper for the given property, or an empty optional if the property was not discovered.Returns the names of the discovered properties, in the order the fields are displayed.List<EasyForm.Field<?>>Returns the configuration wrappers of the discovered properties, in the order the fields are displayed.Validates the form and returns the bean with the current field values written to it.protected booleanincludeProperty(PropertyDescriptor property) Decides whether a discovered property becomes a field.Populates the fields with values from the given bean without live binding.removeButton(Button button) reset()Resets the fields to the values of the last bean set throughsetBean(Object)orreadBean(Object), re-attaching it if it was detached byclear().Binds the given bean to the form in edit mode: fields are populated from the bean and valid value changes are written through to it.setCancelAction(SerializableRunnable cancelAction) Sets the action invoked when the cancel button is clicked, and makes the cancel button visible.setCancelButtonVisible(boolean visible) Overrides the visibility of the cancel button.setComponentFactory(Class<P> propertyType, SerializableSupplier<C> factory, Converter<V, P> converter) Registers a component factory for the given property type in this form instance, together with a converter that adapts the component presentation type to the property type, overriding the global defaults.setComponentFactory(Class<V> type, SerializableSupplier<C> factory) Registers a component factory for the given value type in this form instance, overriding the global defaults.setDefaultComponentFactory(Class<P> propertyType, SerializableSupplier<C> factory, Converter<V, P> converter) Registers a global default component factory for the given property type, together with a converter that adapts the component presentation type to the property type (e.g. aNumberFieldwhoseDoublevalue is converted to aLongproperty).setDefaultComponentFactory(Class<V> type, SerializableSupplier<C> factory) Registers a global default component factory for the given value type.voidsetEnabled(boolean enabled) Enables or disables the form.setFieldOrder(String... propertyNames) Sets the display order of the fields.setI18n(EasyForm.EasyFormI18n i18n) Sets the texts of the button bar.setLabelGenerator(SerializableFunction<String, String> labelGenerator) Sets the function that generates the label of every field whose label was not set explicitly throughEasyForm.Field.withLabel(String), replacing the default derivation from the property name.setResponsiveSteps(FormLayout.ResponsiveStep... steps) Configures the responsive steps of the internal form layout.setSaveAction(SerializableConsumer<T> saveAction) Sets the action invoked with the validated bean when the save button is clicked, and makes the save button visible.setSaveButtonVisible(boolean visible) Overrides the visibility of the save button.setVisibleFields(String... propertyNames) Restricts the form to the given properties: every field that is not listed is excluded, and every listed field that was excluded is included again.validate()Validates every bound field and returns the resulting status, which carries the individual error messages.Methods inherited from class com.vaadin.flow.component.Composite
getChildren, getContent, getElement, initContentMethods inherited from class com.vaadin.flow.component.Component
addListener, findAncestor, fireEvent, from, get, getEventBus, getId, getListeners, getLocale, getParent, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getUI, hasListener, isAttached, isTemplateMapped, isVisible, onAttach, 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.DetachNotifier
addDetachListenerMethods 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.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, setClassName
-
Constructor Details
-
EasyForm
Creates a form whose fields are generated from the properties of the given bean type.- Parameters:
beanType- the bean type to generate the form for, notnull- Throws:
NullPointerException- ifbeanTypeisnullIllegalArgumentException- ifbeanTypecannot be introspected
-
-
Method Details
-
setDefaultComponentFactory
public static <V,C extends Component & HasValue<?, void setDefaultComponentFactoryV>> (Class<V> type, SerializableSupplier<C> factory) Registers a global default component factory for the given value type. The factory applies to allEasyForminstances created after this call, unless overridden per form instance or per property.The built-in type mappings (e.g.
StringtoTextField) are registered through this same registry and can be replaced by calling this method.- Type Parameters:
V- the value typeC- the component type, which must be aComponentand aHasValueof the value type- Parameters:
type- the value type to register the factory for, notnullfactory- the factory that creates a component for the type, notnull- Throws:
NullPointerException- iftypeorfactoryisnull
-
setDefaultComponentFactory
public static <V,P, void setDefaultComponentFactoryC extends Component & HasValue<?, V>> (Class<P> propertyType, SerializableSupplier<C> factory, Converter<V, P> converter) Registers a global default component factory for the given property type, together with a converter that adapts the component presentation type to the property type (e.g. aNumberFieldwhoseDoublevalue is converted to aLongproperty). The factory applies to allEasyForminstances created after this call, unless overridden per form instance or per property.- Type Parameters:
V- the presentation value type of the created componentsP- the property typeC- the component type, which must be aComponentand aHasValueof the presentation value type- Parameters:
propertyType- the property type to register the factory for, notnullfactory- the factory that creates a component for the type, notnullconverter- the converter from the presentation type to the property type, notnull- Throws:
NullPointerException- ifpropertyType,factoryorconverterisnull
-
setComponentFactory
public <V,C extends Component & HasValue<?, void setComponentFactoryV>> (Class<V> type, SerializableSupplier<C> factory) Registers a component factory for the given value type in this form instance, overriding the global defaults. Fields already generated for properties of this type are recreated, unless a custom component was set for them viaEasyForm.Field.withComponent(C).- Type Parameters:
V- the value typeC- the component type, which must be aComponentand aHasValueof the value type- Parameters:
type- the value type to register the factory for, notnullfactory- the factory that creates a component for the type, notnull- Throws:
NullPointerException- iftypeorfactoryisnull
-
setComponentFactory
public <V,P, void setComponentFactoryC extends Component & HasValue<?, V>> (Class<P> propertyType, SerializableSupplier<C> factory, Converter<V, P> converter) Registers a component factory for the given property type in this form instance, together with a converter that adapts the component presentation type to the property type, overriding the global defaults. Fields already generated for properties of this type are recreated, unless a custom component was set for them viaEasyForm.Field.withComponent(C).- Type Parameters:
V- the presentation value type of the created componentsP- the property typeC- the component type, which must be aComponentand aHasValueof the presentation value type- Parameters:
propertyType- the property type to register the factory for, notnullfactory- the factory that creates a component for the type, notnullconverter- the converter from the presentation type to the property type, notnull- Throws:
NullPointerException- ifpropertyType,factoryorconverterisnull
-
configureField
Returns the configuration wrapper for the field generated for the given property.The presentation value type of a field cannot be inferred from a property name, so the wrapper is returned with a wildcard type. The state, presentation and layout methods chain as usual, as does
EasyForm.Field.withComponent(C), which takes its type from its argument. The two methods that take the presentation value type as a parameter,EasyForm.Field.withValidator(Validator)andEasyForm.Field.withConverter(Converter), are only callable on a wrapper obtained throughconfigureField(String, Class).- Parameters:
propertyName- the name of the bean property- Returns:
- the field wrapper
- Throws:
IllegalArgumentException- if no property with the given name was discovered
-
configureField
Returns the configuration wrapper for the field generated for the given property, typed to the given presentation value type. The type is inferred from the argument, so the wrapper can be configured in a fluent chain without an explicit type argument:form.configureField("email", String.class).withValidator(new EmailValidator("Invalid email"));The given type is checked against the value type of the component currently generated for the property, which is the type the field's validators and converters see. Note that this is the presentation type and not necessarily the property type: a
Longproperty bound through the built-inNumberFieldfactory has a presentation type ofDouble. The check is skipped for properties without a component, and for components whose value type cannot be resolved (such asComboBox).Because the check reflects the component in place at the time of the call, replacing the component for a property is done through
configureField(String)andEasyForm.Field.withComponent(C), which types the returned wrapper after the new component.- Type Parameters:
V- the presentation value type of the field- Parameters:
propertyName- the name of the bean propertyvalueType- the expected presentation value type, notnull- Returns:
- the field wrapper
- Throws:
NullPointerException- ifvalueTypeisnullIllegalArgumentException- if no property with the given name was discovered, or if the component of the property does not have the given presentation value type
-
field
Returns the component generated for the given property, which is whatconfigureField(String)wraps. Use this when the component itself is wanted rather than its configuration:TextField email = (TextField) form.field("email");- Parameters:
propertyName- the name of the bean property- Returns:
- the component, or
nullif the property type has no component factory - Throws:
IllegalArgumentException- if no property with the given name was discovered
-
field
Returns the component generated for the given property, typed to the given presentation value type:HasValue<?, String> email = form.field("email", String.class);- Type Parameters:
V- the presentation value type of the component- Parameters:
propertyName- the name of the bean propertyvalueType- the expected presentation value type, notnull- Returns:
- the component, or
nullif the property type has no component factory - Throws:
NullPointerException- ifvalueTypeisnullIllegalArgumentException- if no property with the given name was discovered, or if the component of the property does not have the given presentation value type- See Also:
-
findField
Returns the configuration wrapper for the given property, or an empty optional if the property was not discovered. This is the non-throwing counterpart ofconfigureField(String).- Parameters:
propertyName- the name of the bean property- Returns:
- the field wrapper, or an empty optional
-
getFieldNames
Returns the names of the discovered properties, in the order the fields are displayed.- Returns:
- an unmodifiable list of property names
-
getFields
Returns the configuration wrappers of the discovered properties, in the order the fields are displayed. Excluded fields are included in the result.- Returns:
- an unmodifiable list of field wrappers
-
setFieldOrder
Sets the display order of the fields. The listed properties are shown first, in the given order, followed by every field that was not listed, in declaration order. Repeated names are ignored after their first occurrence.This method only reorders: a field left out of the list is still shown and still bound. Use
setVisibleFields(String...)to restrict which fields the form shows.- Parameters:
propertyNames- the names of the properties to display first, in order- Returns:
- this, for method chaining
- Throws:
NullPointerException- if the array or any of its elements isnullIllegalArgumentException- if any property name is unknown
-
setVisibleFields
Restricts the form to the given properties: every field that is not listed is excluded, and every listed field that was excluded is included again. This is the bulk complement ofEasyForm.Field.excluded()and operates on the same per-field state, so an individual field can still be brought back afterwards withEasyForm.Field.visible(). A listed field that is read-only stays read-only.The display order is unaffected ? use
setFieldOrder(String...)for that.- Parameters:
propertyNames- the names of the properties to show- Returns:
- this, for method chaining
- Throws:
NullPointerException- if the array or any of its elements isnullIllegalArgumentException- if any property name is unknown
-
setBean
Binds the given bean to the form in edit mode: fields are populated from the bean and valid value changes are written through to it.- Parameters:
bean- the bean to edit, ornullto clear the form- Returns:
- this, for method chaining
- Throws:
IllegalStateException- if a field has a component whose value type cannot be written to its property and no converter was set for it
-
readBean
Populates the fields with values from the given bean without live binding. Changes are not written to the bean untilgetValidBean()or the save action runs.- Parameters:
bean- the bean to read values from, ornullto clear the form- Returns:
- this, for method chaining
- Throws:
IllegalStateException- if a field has a component whose value type cannot be written to its property and no converter was set for it
-
getValidBean
Validates the form and returns the bean with the current field values written to it. When no bean is attached ? because none was set, or becauseclear()detached it ? the values are written to a new instance, and the bean type must have an accessible no-args constructor.Use
validate()instead when the reason for a validation failure is needed.- Returns:
- the populated bean, or an empty optional if validation failed
- Throws:
IllegalStateException- if no bean has been set and the bean type cannot be instantiated, or if a field has a component whose value type cannot be written to its property and no converter was set for it
-
validate
Validates every bound field and returns the resulting status, which carries the individual error messages. Nothing is written to a bean, so this can be called to drive the state of the surrounding UI. FollowsBinder.validate()semantics.- Returns:
- the validation status
- Throws:
IllegalStateException- if a field has a component whose value type cannot be written to its property and no converter was set for it
-
addStatusChangeListener
Adds a listener notified whenever the validation status of the form changes, which is the supported way to keep the surrounding UI ? a save button, a summary ? in step with the form. The event carries the errors, and its binder answershasChanges()for dirty state.form.addStatusChangeListener( event -> saveButton.setEnabled(!event.hasValidationErrors() && event.getBinder() .hasChanges()));- Parameters:
listener- the listener to add, notnull- Returns:
- a registration for removing the listener
-
addValueChangeListener
public Registration addValueChangeListener(HasValue.ValueChangeListener<? super HasValue.ValueChangeEvent<?>> listener) Adds a listener notified whenever the value of any bound field changes, whether or not the new value is valid.- Parameters:
listener- the listener to add, notnull- Returns:
- a registration for removing the listener
-
reset
Resets the fields to the values of the last bean set throughsetBean(Object)orreadBean(Object), re-attaching it if it was detached byclear(). If no bean was ever set, all fields are cleared.- Returns:
- this, for method chaining
-
clear
Clears all fields and detaches the current bean, so that the cleared values are never written to it: after this callgetBean()returnsnullandgetValidBean()writes to a new instance. The last bean set is remembered and can be restored withreset().- Returns:
- this, for method chaining
-
addBeanValidator
Adds a bean-level (cross-field) validator. Bean validators run after all field-level validators have passed.- Parameters:
validator- the bean validator to add, notnull- Returns:
- this, for method chaining
- Throws:
NullPointerException- ifvalidatorisnull
-
setSaveAction
Sets the action invoked with the validated bean when the save button is clicked, and makes the save button visible.- Parameters:
saveAction- the save action, ornullto remove it- Returns:
- this, for method chaining
-
setCancelAction
Sets the action invoked when the cancel button is clicked, and makes the cancel button visible.- Parameters:
cancelAction- the cancel action, ornullto remove it- Returns:
- this, for method chaining
-
setI18n
Sets the texts of the button bar.- Parameters:
i18n- the texts to use, notnull- Returns:
- this, for method chaining
- Throws:
NullPointerException- ifi18nisnull
-
setLabelGenerator
Sets the function that generates the label of every field whose label was not set explicitly throughEasyForm.Field.withLabel(String), replacing the default derivation from the property name. Passnullto restore the default.This is the bulk counterpart of overriding
createLabel(String), and applies immediately to the fields already generated.- Parameters:
labelGenerator- the generator, taking a property name and returning a label, or- Returns:
- this, for method chaining
nullfor the default
-
setSaveButtonVisible
Overrides the visibility of the save button. By default the button is visible if and only if a save action has been set.- Parameters:
visible- whether the save button is visible- Returns:
- this, for method chaining
-
setCancelButtonVisible
Overrides the visibility of the cancel button. By default the button is visible if and only if a cancel action has been set.- Parameters:
visible- whether the cancel button is visible- Returns:
- this, for method chaining
-
addButton
public Button addButton(String text, ComponentEventListener<ClickEvent<Button>> clickListener, ButtonVariant... variants) Adds an extra button to the button bar.- Parameters:
text- the button textclickListener- the click listenervariants- the theme variants to apply, if any- Returns:
- the added button
-
addButton
public Button addButton(String text, Component icon, ComponentEventListener<ClickEvent<Button>> clickListener, ButtonVariant... variants) Adds an extra button with an icon to the button bar.- Parameters:
text- the button texticon- the button iconclickListener- the click listenervariants- the theme variants to apply, if any- Returns:
- the added button
-
removeButton
Removes a button previously added withaddButton(java.lang.String, com.vaadin.flow.component.ComponentEventListener<com.vaadin.flow.component.ClickEvent<com.vaadin.flow.component.button.Button>>, com.vaadin.flow.component.button.ButtonVariant...).- Parameters:
button- the button to remove, notnull- Returns:
- this, for method chaining
- Throws:
NullPointerException- ifbuttonisnullIllegalArgumentException- if the button is not in the button bar, or is the save or cancel button ? usesetSaveButtonVisible(boolean)orsetCancelButtonVisible(boolean)for those
-
setEnabled
public void setEnabled(boolean enabled) Enables or disables the form. In addition to disabling the generated components and the button bar ? which the inheritedHasEnabledbehaviour already does, since they are all in this component's element tree ? this makes every binding read-only, so that a disabled form cannot be written to the bean even programmatically.Re-enabling restores the read-only state each field was configured with, so a field made read-only through
EasyForm.Field.readOnly()stays read-only.- Specified by:
setEnabledin interfaceHasEnabled- Parameters:
enabled- whether the form is enabled
-
setResponsiveSteps
Configures the responsive steps of the internal form layout.- Parameters:
steps- the responsive steps- Returns:
- this, for method chaining
- See Also:
-
includeProperty
Decides whether a discovered property becomes a field. Called during construction for every property that has both a getter and a setter; returningfalseleaves the property out of the form and the binding entirely. The default implementation accepts every property.- Parameters:
property- the property being considered- Returns:
- whether to generate a field for the property
-
createComponent
Creates the component for a property that has no registered component factory. The default implementation returns aComboBoxof the constants for an enum property, andnullfor anything else, which leaves the property out of the form with a logged warning.Components for property types that do have a factory are not created here ? override them with
setComponentFactory(Class, SerializableSupplier), which also carries the converter, or per property withEasyForm.Field.withComponent(C). The precedence iswithComponent> registered factory > this method.- Parameters:
propertyName- the name of the propertypropertyType- the property type, with primitives already wrapped- Returns:
- the component to use, or
nullto leave the property out of the form
-
createLabel
Returns the label for a property whose label was not set explicitly throughEasyForm.Field.withLabel(String). The default implementation applies the function given tosetLabelGenerator(SerializableFunction), or derives the label from the property name when there is none.- Parameters:
propertyName- the name of the property- Returns:
- the label to use
-
configureComponent
Called after a generated component has been created and its label, placeholder and helper text applied, for decoration that applies to every field ? style names, widths, theme variants. The default implementation does nothing. Not called for components set throughEasyForm.Field.withComponent(C).- Parameters:
propertyName- the name of the property the component was generated forcomponent- the generated component
-