AbstractView
Introduction
AbstractView is the principal template ( Template , GoF) of JDAL for dealing with forms.
AbstractView permits you condense the development work of forms in one unique job: Implement the method buildPanel().
AbstractView supports the automatic way of the following commune jobs in the development of Vaadin forms:
- Automatic Binding: between the user interface controls and the domain models. Following simple rules of nomenclature, could eliminate completely in code the data binding in the making of the forms.
- Validating the model: Actually the unique supported interface is org.springframework.validation.Validator. But this is enough to use JSR-303 annotations.
- Processing validation errors: The interface ErrorProcessor represents a contract for the processing of the validation errors. The default implementation, BackgroundErrorProcessor adds a tooltip with informatión about the error.
- Nesting or grouping of other views: AbstractView supports the delegatión of the operations towards other views by SubViews (Views on the same model) or views of the properties of the model
- Modification status of the controls: The method isDirty() proporcions information about the state of the modification of the view controls.
- Activate/Deactivate all the view controls: Simply call the method enableView(boolean value) to enable or disable the control on which there is a binding.
- Initialization of the controls: AbstractView detects JPA annotations in the form backup model and could initialise the data controls in an automatic way.
The process of building a form based on Abstractview can be summarised in the following steps:
- Extend the class AbstracView parameterized with the model type on which the form back ups the data.
- Create the Component that contains the form in the buildPanel() method.
- Calling at somepoint the method autobind(), generally by the configuration of a method of initializatión in the IoC container (init-method, @PostConstruct, @Autowire...).
Data Binding
AbstractView supports at the moment two forms of realizing the binding of data between user interface controls and the domain models:
- Manual: For binding manual we refer to the need to call at any moment, the method bind(Object control, String propertyName) to associate a model property to a user interface control.
- Automatic: In automátic mode, AbstractView make the association between controls and the properties of the model via the method autobind()to associate the view controls with the properties of the model of the same name. That's to say, if the model contains a property name which fulfills the specification Java Beans and you want to associate the property to a TextField in view, simply declare the TextField with the same name in the view, and call the method autobind() sometime.
In both cases, in creating binding between a Vaadin component and a property of the model, it will undertake the following actions:
- The form will recieve notifications about changes in the control via the method controlChange(). The default implementation only changes the state of the form to dirty. You can override this method to personalise the behaviour. Remember call the method of the super class if you want to maintain this behaviour.
- Initalizatión of the controls. The Vaadin controls can be initialized by the interface ControlInitializer. The default implementaion detects JPA and JDAL annotations.
Finally, AbstractView provides three template methods to customize the data binding process:
- doRefresh(): called from refresh()
- doUpdate(): called from update().
- onSetModel(): called from setModel().
Structure
AbstractView delegates much of the work in strategies or support classes of JDAL. It is interesting to take a look as you might be interested in using them directly o personalising the behaviours.
CompositeBinder
Performs all work in data binding between the conttols and models. It can be used independently outside of AbstractView. It is not necessary to extend JDAL classes to dispose of data binding system. You can find more information in the data binding chapter..
ErrorProcessor
Processing strategy of binding errors. The implementation by default, BackgroundErrorProcessor changes the colour of the background of the controls that have failed and add a tooltip with the information of the error.
BinderFactory
PropertyBinder factory, CompositeBinder uses it to find the Binder appropriate for each type of control.
ControlAccessorFactory
Allow access to generic Swing controls. That is to say, perform control operations without the specific knowledge of the type of control.
ControlInitializer
Strategy of control initialization, usually with data from the respositories or other entities. The implementatión by default DefaultControlInitializer is able to detect annotationes JPA and the annotation @Reference of JDAL Core.
Example
Model
We will create a View for the following Book entity:
@Entity @Table(name="books") public class Book implements Serializable { @Id @GeneratedValue private Long id; @NotEmpty private String name = ""; @NotNull @ManyToOne @JoinColumn(name="authorid") private Author author; @NotNull @ManyToOne @JoinColumn(name="categoryid") private Category category; private String isbn = ""; private Date publishedDate; public String toString() { return name; } // Getters and Setters }
View
The target objective is to create a form to edit books with data binding, internacionalization and validation of the backing model
The following is the code of the book editor form using AbstracView
public class BookView extends AbstractView<Book> { // We use autobind jdal feature, name the controls like Book properties private TextField name = new TextField(); private TextField isbn = new TextField(); @Property(name="publishedDate") // map to publishedDate model property (the default) private DateField publishedDate = new DateField(); @Initializer(orderBy="name") // autofill with authors, sorted by name private ComboBox author = new ComboBox(); @Initializer(orderBy="name") // autofill with categories, sorted by name private ComboBox category = new ComboBox(); public BookView() { this(new Book()); } public BookView(Book model) { super(model); } @PostConstruct public void init() { // Just our binding code! autobind(); } @Override protected Component buildPanel() { BoxFormBuilder fb = new BoxFormBuilder(); fb.setDefaultWidth(SimpleBoxFormBuilder.SIZE_FULL); fb.setFixedHeight(); fb.row(); fb.add(name, getMessage("Book.title")); fb.row(); fb.startBox(); fb.row(); fb.add(author, getMessage("Book.author")); fb.add(category, getMessage("Book.category")); fb.endBox(); fb.row(); fb.startBox(); fb.row(); fb.add(isbn, getMessage("Book.isbn")); fb.add(publishedDate, getMessage("Book.publishedDate"), 120); fb.endBox(); return fb.getForm(); } // The view name is shown as title on ViewDialog. @Override public String getName() { Book model = getModel(); if (model != null && model.getId() != null) { return model.getName(); } return null; }
Context Configuration
The book editor looks simple, but need some spring configuration to work. the most important is the abstract view bean definition that inject the validator, error processor and control initializer to views. Instead declaring all views in context bean definition files, we could use the JDAL @Parent annotation to inherit xml abstract bean definition when using the Spring auto scan packages for beans feature.<!-- Register default Vaadin components --> <vaadin:defaults /> <!-- Vaadin scoped beans are linked to UI class instances, Use with care, is still a beta component. --> <bean id="vaadinScopeConfigurer" class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="ui" value="org.jdal.vaadin.beans.VaadinScope" /> </map> </property> </bean> <!-- Abstract definition for Views --> <bean id="view" abstract="true"> <property name="controlInitializer" ref="controlInitializer"/> <property name="errorProcessors"> <list> <ref bean="errorProcessor" /> </list> </property> <property name="validator" ref="validator" /> </bean> <!-- Default control initializer --> <bean id="controlInitializer" class="org.jdal.vaadin.ui.bind.VaadinControlInitializer"> <property name="persistentService" ref="contextPersistentService" /> </bean> <!-- Spring JSR-303 validator --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <jdal:serializable-proxy /> </bean> <!-- Error Processor --> <bean id="errorProcessor" class="org.jdal.vaadin.ui.bind.UserErrorProcessor" /> <!-- View Dialog Prototype --> <bean id="viewDialog" class="org.jdal.vaadin.ui.form.ViewDialog" scope="prototype"/> <!-- Book Form Editor --> <bean id="bookEditor" class="org.jdal.samples.vaadin.BookView" parent="view" scope="prototype" > <property name="width" value="600" /> <property name="height" value="300" /> <property name="persistentService" ref="bookService" /> </bean> <!-- Book DAO --> <jdal:service entity="org.jdal.samples.model.Book" />
Now, we can get instances of the project editor by the bean "bookEditor".
For a full sample application see vaadin-sample-jpa.