Important classes/interfaces of the library

These basic interfaces of the library will be described in detail in later chapters.

Data binding

Quite powerful natural data binding is implemented. Immutable objects are supported. Instantiation of the objects is abstracted using an Instantiator interface.

Form definition

Basic mapping with specification of whitelist of edited properties

Basic mapping can contain these kinds of definitions:

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")
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")

Automatic mapping of properties

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())

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:

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")

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):

Most important properties of FormField:

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