Forms¶
Overview¶
Vaadin provides some support for Forms with Binder
, but Krail takes that further. It makes the definition of a Form part of the Sitemap by assigning a FormConfiguration
to a SitemapNode
.
The Form class takes that configuration and builds the form with UI components (TextField etc) and integrates Krail’s I18N and JSR 303 validation.
Two Form types are currently provided:
- simple, which displays/edits selected properties form a given entity class
- list, which displays a table of selected properties, for one more instance of the same entity class
Additional form types can easily be added.
Defining a Form¶
To construct a form in Krail (using a Person entity as an example):
- Define your form configuration as a sub-class of
FormConfiguration
, for examplePersonFormConfiguration
- using either the Direct or Annotation method of creating a
Sitemap
entry, set the viewClass toForm.class
- set the viewConfiguration to
PersonFormConfiguration.class
Form construction¶
As part of Krail’s navigation process, the view for a given URI is looked up from the Sitemap. The viewClass is constructed via Guice, and an instance of the viewConfiguration passed to it (in this example an instance of PersonFormConfiguration
Form
invokesFormTypeSelector
to acquire the correctFormBuilder
FormBuilder
usesFormConfiguration
in combination withFormSupport
to construct appropriate UI components (TextFields etc) and bind them to enity data. The binding is carried out byKrailBeanValidationBinder
, which also takes care of integrating JSR303 validation and I18N.
Validation¶
Validation can be defined by JSR303 annotations on the entity or directly within the FormConfiguration
Model to Presentation mapping¶
FormSupport
provides the mappings of data types to presentation Fields, along with data converters. These mappings are defined in Guice and can therefore be easily extended or overruled.
Defaults¶
For each property (the Model) that is being bound to the user interface, a component (the Presentation) is needed. To enable the automatic creation of presentation elements, FormSupport.fieldFor()
uses a map of data types to Vaadin Fields - for example, a String is mapped to a TextField
.
Default mappings are provided by the FormModule
, but these can be overridden for specific instances within FormConfiguration
.
Changing defaults¶
To change the default Model to Presentation mappings, sub-class FormModule
override bindDefaultDataClassMappings
, and replace FormModule
with your sub-class in your BindingsCollator
Register a new mapping¶
A new data type can be registered, by creating another Guice module which contributes a MapBinder
as below
class MyMappingModule : AbstractModule() {
override fun configure() {
val fieldLiteral = object : TypeLiteral<AbstractField<*>>() {}
val dataClassLiteral = object : TypeLiteral<Class<*>>() {}
val dataClassToFieldMap: MapBinder<Class<*>, AbstractField<*>> = MapBinder.newMapBinder(binder(), dataClassLiteral, fieldLiteral)
// bind new data types
dataClassToFieldMap.addBinding(MyDataClass::class.java).to(WidgetField::class.java)
}
Model to Presentation Converters¶
Where the model and presentation type are the same, clearly no conversion is needed, although a NoConversionConverter
is actually used to transfer the data.
Where a converter is needed - for example, to display an integer in a TextField, a StringToIntegerConverter
is needed - this converter type is provided by FormSupport.converterFor()
Ultimately this uses an implementation of ConverterFactory
to instantiate the converter itself. The default implementation DefaultConverterFactory
, iterates through all ConverterSets
classes defined via Guice until it finds one to match the desired model and presentation class, or throws an exception if none found.
By default there is just one ConverterSet
, the BaseConverterSet
.
Adding / Replacing Converters¶
Converters can be added by defining your own ConverterSet
, and adding it in one of two ways:
- sub-class
ConverterModule
and override thedefine()
method to provide bindings to additionalConverterSet
implementations, and replaceConverterModule
in yourBindingsCollator
- create a new module using
ConverterModule
as an example (in particular the MultiBinder), and add the new module to yourBindingsCollator