Documentation
Content
Important classes/interfaces of the library
These basic interfaces of the library will be described in detail in later chapters.
- Forms – entry point for the usage of the library
- FormElement - an element of form - form field or group of fields (form mapping)
- FormMapping - a mapping corresponding to one edited class, there are nested mappings for nested classes
- FormField - mappings consist of form fields (and possible nested mappings)
- FieldProps - custom definition can be provided for some chosen form field (using FieldProps)
- RequestParams - preprocessor of request parameters (ServletRequestParams extracts parameters from HttpServletRequest, PortletRequestParams from javax.portlet.ActionRequest)
- ValidationResult - contains validation messages for violated constraints
- ConstraintViolationMessage - object holding information about violation of some constraint
- Config - configuration of the form mapping, default configuration can be overriden by your custom configuration
- Formatters, Formatter - formatters convert values of various data types to/from strings
- CollectionBuilders, CollectionBuilder
- BeanExtractor - extracts properties of an object and data from object's properties
- Instantiator - creates new instances of classes (implementations for constructors, static factory methods are provided)
- Binder - binds data to new/existing instance of an object
- ArgumentNameResolver - resolves names of constructor's arguments
- BeanValidator - validates an object filled with data (whether all specified constraints are met)
- PropertyMethodRegex - regular expression describing getters/setters
- UploadedFile - type into which uploaded files can be bound
- FormRenderer - can be used for automatic rendering of form markup (custom extension can be written). See unit tests for example of usage.
It can already generate the whole form's markup in a default customizable way. This can be especially helpful for bigger applications with consistent style of forms (much writing of markup can be spared).
Data binding
Quite powerful natural data binding is implemented. Immutable objects are supported. Instantiation of the objects is abstracted using an Instantiator interface.
- Setter and "construction methods" binding. Construction methods are:
- Default or non-default constructors,
- static factory methods on specified factory classes.
- Binding to collections and arrays.
- Binding to complex nested objects and lists of complex objects.
- And arbitrary combination recursively.
- Primitives (and their wrapper classes) are supported everywhere.
- String, Enum, BigDecimal, BigInteger, Date types are supported by included formatters, custom formatters can be defined.
Form definition
Basic mapping with specification of whitelist of edited properties
Basic mapping can contain these kinds of definitions:
- Specification of form fields (one form field serves for editing of one property of edited object - for e.g. text field for a number, multichoice select for set of enum values).
- Nested mappings for complex nested objects (for e.g. for contactAddress property of complex type Address).
- Nested list mappings for collections/arrays of complex nested objects (for e.g. for collegues property of complex type List<Collegue>).
Nested mapping can be specified as a part of an outer mapping, or independently (for e.g. stored in its own static variable,
reusable in other form definitions) and just referenced from the outer mapping. All mappings are
immutable and can be freely shared and hold for e.g. in final static variables.
Example of "registration form" definition
private static final FormMapping<RegDate> regDateMapping =
Forms.basic(RegDate.class, "regDate").fields("month", "year")
.build();
private static final FormMapping<Registration> registrationForm =
Forms.basic(Registration.class, "registration")
// whitelist of properties to bind
.fields("attendanceReasons", "cv", "interests", "email")
.nested(Forms.basic(Address.class, "contactAddress",
Forms.factoryMethod(Address.class, "getInstance"))
.fields("street", "city", "zipCode").build())
.nested(Forms.basic(Collegue.class, "collegues", MappingType.LIST)
.fields("name", "email")
.nested(regDateMapping)
.build())
.build();
Automatic mapping of properties
- Introspects and uses readable and settable properties to construct definition of mappings, nested mappings and form fields automatically.
Property is settable if it can be set using a setter or instantiator
(constructor, static factory method). Property is readable if corresponding accessor (getter) exists.
- @Ignored on a getter can be used to exclude some property from definition (and so from the later binding).
- Can be freely combined with basic mappings, also custom field definitions can be used in automatic mapping and take precedence over the automatically introspected fields.
Example of "registration form" definition using automatic mapping
private static final FormMapping<Registration> registrationForm =
Forms.automatic(Registration.class, "registration")
.nested(Forms.automatic(Address.class, "contactAddress",
Forms.factoryMethod(Address.class, "getInstance")).build())
.build();
As you can see, this definition is much shorter than using basic mapping (with explicit whitelist of properties),
but fully equivalent. The only reason why it is still so long is usage of static factory method for instantiation
of an Address instead of a constructor, otherwise it could look as follows:
private static final FormMapping<Registration> registrationForm =
Forms.automatic(Registration.class, "registration").build();
The advantage of automatic mapping of properties is its simplicity and briefness. But basic mapping
that specifies all properties explicitly (as a whitelist) is more safe in that its definition
will throw exception early if some property of edited object is missing or is removed later
without updating the form properly (definition will be not built successfully). Automatic mapping cannot
recognize (introspect) that some property is missing.
Custom form field specification
Other properties besides propertyName can be optionally specified for the form fields using FieldProps class and its builder:
- type - type of form field (textarea, text, checkbox, ...). This information is later available using getter on FormField.
- pattern - pattern used in formatter for converting the value of property to/from string.
- formatter - custom formatter for converting the value of property to/from string.
Example of custom form field specification for birthDate property:
private static final FormMapping<Person> PERSON_FORM = Forms.basic(Person.class, "person")
.fields("personId", "firstName", "lastName", "salary", "phone", "male", "nation")
.field(Forms.<Date>field("birthDate").type("text").formatter(CUSTOM_DATE_FORMATTER).build())
.build();
Custom form field specification can also be used in automatic mapping, your specification will override
field definition from automatic instrospection.
When you only need to specify property name and type of form field, you can just use method field(propertyName, type)
of the mapping builder.
Filling the form with data
By calling fill method on form definition.
FormData<Person> formData = new FormData<Person>(
person, ValidationResult.empty);
FormMapping<Person> filledForm = personForm.fill(formData);
// Push the filled form into a template,
// use its properties to render it or use
// FormRenderer to generate form markup automatically
Data for templates
Just push the filled form (root FormMapping) to the template and use its properties (you can print/debug the filled form
using its toString method to easily see structure of filled form):
- validationResult
- success
- fieldMessages
- globalMessages
- elements - List<FormElement> - both form fields and nested mappings in the form (nested elements of current mapping)
- fields – Map<String, FormField>
- name – unique name/path of mapping
- labelKey – key for caption of nested complex object carried by mapping
- filledObject
- required
- nested – map with nested mappings
- list – list of nested mappings filled with complex objects if this (current) mapping is of MappingType.LIST type
Most important properties of FormField:
- name – unique name/path of form field
- labelKey – key for localization of label (name without brackets)
- filledObject, filledObjects (for group of checkboxes, multiselect)
- visible, enabled, readonly, required
- validationMessages, validators
Both FormMapping(s) and FormField(s) are implementing FormElement interface with common methods/properties.
For e.g. you can use getProperties() method (or corresponding name in the template) to access additional
properties of form mapping or form field - e.g. help, various flags, size of text area and many other
properties that you can define for yourself using property() method on definitions of form fields or mappings.
Binding data from the request back to an object
By calling bind method on form definition.
FormData<Person> formData = personForm.bind(
new ServletRequestParams(request));
if (formData.isValid()) {
// save the person: formData.getData()
} else {
// show again the invalid form with validation
// messages: personForm.fill(formData) ...
}
Further exploration