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
Sitemapentry, 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
ForminvokesFormTypeSelectorto acquire the correctFormBuilderFormBuilderusesFormConfigurationin combination withFormSupportto 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
ConverterModuleand override thedefine()method to provide bindings to additionalConverterSetimplementations, and replaceConverterModulein yourBindingsCollator - create a new module using
ConverterModuleas an example (in particular the MultiBinder), and add the new module to yourBindingsCollator