Creating a CustomField for editing the address of a person
A normal use case is that you want to create a form out a bean that the user can edit. Often these beans contain references to other beans as well, and you have to create a separate editor for those. This tutorial goes through on how to edit an Address
bean which is inside a Person
bean with the use of CustomField
and FieldGroup
.
Here are the Person
and Address
beans
Java
public class Person {
private String firstName;
private String lastName;
private Address address;
private String phoneNumber;
private String email;
private Date dateOfBirth;
private String comments;
//Getters and setters
}
Java
public class Address {
private String street;
private String zip;
private String city;
private String country;
// Getters and setters
}
Creating a new field
The first step is to create a new field which represents the editor for the address. In this case the field itself will be a button. The button will open a window where you have all the address fields. The address will be stored back when the user closes the window.
Java
public class AddressPopup extends CustomField<Address> {
@Override
protected Component initContent() {
return null;
}
@Override
public Class<Address> getType() {
return Address.class;
}
}
CustomField requires that you implement two methods, initContent()
and getType()
. initContent()
creates the actual visual representation of your field. getType()
tells the field which type of data will be handled by the field. In our case it is an Address
object so we return Address.class
in the method.
Creating the content
Next up we create the actual button that will be visible in the person editor when the CustomField is rendered. This button should open up a new window where the user can edit the address.
Java
@Override
protected Component initContent() {
final Window window = new Window("Edit address");
final Button button = new Button("Open address editor", new ClickListener() {
public void buttonClick(ClickEvent event) {
getUI().addWindow(window);
}
});
return button;
}
This is enough to attach the field to the person editor, but the window will be empty and it won’t modify the data in any way.
Creating the editable fields
The address object contains four strings - street, zip, city and country. For the three latter a TextField
is good for editing, but the street address can contain multiple row so a TextArea
is better here. All the fields have to be put into a layout and the layout has to be set as the content of the window. FormLayout
is a good choice here to nicely align up the captions and fields of the window.
Java
FormLayout layout = new FormLayout();
TextArea street = new TextArea("Street address:");
TextField zip = new TextField("Zip code:");
TextField city = new TextField("City:");
TextField country = new TextField("Country:");
layout.addComponent(street);
layout.addComponent(zip);
layout.addComponent(city);
layout.addComponent(country);
window.setContent(layout);
The field is now visually ready but it doesn’t contain or affect any data. You want to also modify the sizes as well to make it look a bit nicer:
Java
window.center();
window.setWidth(null);
layout.setWidth(null);
layout.setMargin(true);
Binding the address to the field
A FieldGroup can be used to bind the data of an Address bean into the fields. We create a member variable for a FieldGroup and initialize it within the createContent() -method:
Java
fieldGroup = new BeanFieldGroup<Address>(Address.class);
fieldGroup.bind(street, "street");
fieldGroup.bind(zip, "zip");
fieldGroup.bind(city, "city");
fieldGroup.bind(country, "country");
The FieldGroup
of the person editor will call AddressPopup.setValue(person.getAddress())
when we start to edit our person. We need to override setInternalValue(Address)
to get the Address
object and pass it to the FieldGroup
of the address editor.
Java
@Override
protected void setInternalValue(Address address) {
super.setInternalValue(address);
fieldGroup.setItemDataSource(new BeanItem<Address>(address));
}
The last thing that has to be done is save the modifications made by the user back into the Address
bean. This is done with a commit()
call to the FieldGroup
, which can be made for example when the window is closed:
Java
window.addCloseListener(new CloseListener() {
public void windowClose(CloseEvent e) {
try {
fieldGroup.commit();
} catch (CommitException ex) {
ex.printStackTrace();
}
}
});
Now you need to attach the AddressPopup
custom field into the person editor through it’s FieldGroup
and you have a working editor.
Complete code
Java
package com.example.addressforms.fields;
import com.example.addressforms.data.Address;
import com.vaadin.data.fieldgroup.BeanFieldGroup;
import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
import com.vaadin.data.util.BeanItem;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomField;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.CloseEvent;
import com.vaadin.ui.Window.CloseListener;
public class AddressPopup extends CustomField<Address> {
private FieldGroup fieldGroup;
@Override
protected Component initContent() {
FormLayout layout = new FormLayout();
final Window window = new Window("Edit address", layout);
TextArea street = new TextArea("Street address:");
TextField zip = new TextField("Zip code:");
TextField city = new TextField("City:");
TextField country = new TextField("Country:");
layout.addComponent(street);
layout.addComponent(zip);
layout.addComponent(city);
layout.addComponent(country);
fieldGroup = new BeanFieldGroup<Address>(Address.class);
fieldGroup.bind(street, "street");
fieldGroup.bind(zip, "zip");
fieldGroup.bind(city, "city");
fieldGroup.bind(country, "country");
Button button = new Button("Open address editor", new ClickListener() {
public void buttonClick(ClickEvent event) {
getUI().addWindow(window);
}
});
window.addCloseListener(new CloseListener() {
public void windowClose(CloseEvent e) {
try {
fieldGroup.commit();
} catch (CommitException ex) {
ex.printStackTrace();
}
}
});
window.center();
window.setWidth(null);
layout.setWidth(null);
layout.setMargin(true);
return button;
}
@Override
public Class<Address> getType() {
return Address.class;
}
@Override
protected void setInternalValue(Address address) {
super.setInternalValue(address);
fieldGroup.setItemDataSource(new BeanItem<Address>(address));
}
}