Important Notice - Forums is archived
To simplify things and help our users to be more productive, we have archived the current forum and focus our efforts on helping developers on Stack Overflow. You can post new questions on Stack Overflow or join our Discord channel.

Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.
Subforms in Nested Bean Validation
Hi All,
I made a dummy application to test vaadin framework. I wanted to tes Nested Form. For which i followed this code
http://demo.vaadin.com/book-examples/book/1_7/#component.form.subform.nestedforms
I have following simple entities
Qualification
----- String descriptor [Not NULL]
Address
----- String city [Not NULL]
PersonAddress (table in between)
----- Integer personid [Not NULL]
----- Integer addressid [Not NULL]
Person
----- String name; [Not NULL]
----- Qualification qualid; [Not NULL]
----- List<personAddress> addressList;
Here is my Person Editor
public class PersonEditor extends VerticalLayout {
private final PersonRepository repository;
private TextField name = new TextField("name");
private ComboBox qualid = new ComboBox("Qualification");
private AddressFormTable personAddressList = new AddressFormTable();
Button save = new Button("Save", FontAwesome.SAVE);
Button cancel = new Button("Cancel");
Button delete = new Button("Delete", FontAwesome.TRASH_O);
CssLayout actions = new CssLayout(save, cancel, delete);
private BeanFieldGroup<Person> personFG = new BeanFieldGroup<>(Person.class);
@Autowired
public PersonEditor(PersonRepository repository, QualificationRepository qualRepos, final ChangeHandler h) {
this.repository = repository;
personFG.setBuffered(false);
name.setNullRepresentation("");
name.setRequired(true);
addComponents(name, qualid, personAddressList, actions);
qualid.setContainerDataSource(new BeanItemContainer(Qualification.class, qualRepos.findAll()));
setSpacing(true);
actions.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
save.setStyleName(ValoTheme.BUTTON_PRIMARY);
save.setClickShortcut(ShortcutAction.KeyCode.ENTER);
save.addClickListener(new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
if(personFG.isValid())
{
repository.save(personFG.getItemDataSource().getBean());
h.onChange();
} else {
Notification.show("validation error!",Type.TRAY_NOTIFICATION);
}
}
});
delete.addClickListener( new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
repository.delete(personFG.getItemDataSource().getBean());
h.onChange();
}
});
cancel.addClickListener(e -> editPerson(personFG.getItemDataSource().getBean()));
setVisible(false);
}
public interface ChangeHandler {
void onChange();
}
// ENTRY POINT
public final void editPerson(Person c) {
final boolean persisted = c.getId() != null;
Person person;
if (persisted) {
person = repository.findOne(c.getId());
}
else {
person = c;
}
BeanItem<Person> personBeanItem = new BeanItem<Person>(person);
cancel.setVisible(persisted);
personFG.setItemDataSource(personBeanItem);
personFG.bindMemberFields(this);
setVisible(true);
save.focus();
name.selectAll();
}
class AddressFormTable extends CustomField {
Table table = new Table();
[b]BeanItemContainer<PersonAddress> personAddContainer = new BeanItemContainer<PersonAddress>(PersonAddress.class);[/b]
VerticalLayout layout = new VerticalLayout();
@Override
protected com.vaadin.ui.Component initContent() {
[b] table.addContainerProperty("form", FormLayout.class, null);[/b]
table.setColumnHeaderMode(ColumnHeaderMode.HIDDEN);
layout.addComponent(table);
Button newMoon = new Button ("New Address");
newMoon.addClickListener(new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
[b]table.removeAllItems();
for (PersonAddress itemId: personAddContainer.getItemIds())
addItemToTable(itemId);[/b]
PersonAddress pa = new PersonAddress();
Address add = new Address();
pa.setAddressid(add);
pa.setPersonid(personFG.getItemDataSource().getBean());
personFG.getItemDataSource().getBean().getPersonAddressList().add(pa);
BeanItem<PersonAddress> itemId = personAddContainer.addBean(pa);
addItemToTable(itemId.getBean());
table.setPageLength(personAddContainer.size() == 0 ? 1: personAddContainer.size());
}
});
table.setPageLength(1);
table.setHeightUndefined();
layout.setWidth("100%");
layout.addComponent(newMoon);
layout.setComponentAlignment(newMoon, Alignment.BOTTOM_LEFT);
return layout;
}
void addItemToTable (PersonAddress itemId) {
AddressForm form = new AddressForm(itemId);
[b]BeanItem<PersonAddress> bi = personAddContainer.getItem(itemId);
if(bi.getItemProperty("addressid") != null)
{
bi.expandProperty("addressid");
}
BeanFieldGroup<PersonAddress> beanFieldGroup = new BeanFieldGroup<PersonAddress>(PersonAddress.class);
beanFieldGroup.setBuffered(false);[/b]
[b]beanFieldGroup.setItemDataSource(bi);
beanFieldGroup.bindMemberFields(form);
table.addItem(new Object[]{form}, itemId);[/b]
}
@Override
public Class<?> getType() {
return List.class;
}
@Override
public void setPropertyDataSource(Property newDataSource) {
Object value = newDataSource.getValue();
if (value instanceof List) {
@SuppressWarnings("unchecked")
List beans = (List) value;
[b]personAddContainer.removeAllItems();
personAddContainer.addAll(beans);
table.removeAllItems();
for (PersonAddress itemId: personAddContainer.getItemIds())
addItemToTable(itemId);[/b]
table.setPageLength(beans.size());
super.setPropertyDataSource(newDataSource);
} else
throw new ConversionException("Invalid type");
}
class AddressForm extends FormLayout {
@PropertyId("addressid.city")
TextField city ;
Button delete = new Button("Delete", FontAwesome.TRASH_O);
public AddressForm(final PersonAddress itemId) {
city = new TextField("City");
city.setImmediate(true);
delete.addClickListener( new ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
PersonAddress pa = personAddContainer.getItem(itemId).getBean();
pa.getPersonid().getPersonAddressList().remove(pa);
pa.setPersonid(null);
pa.setAddressid(null);
personAddContainer.removeItem(itemId);
table.removeItem(itemId);
}
});
addComponent(city);
addComponent(delete);
setSizeUndefined();
city.setNullRepresentation("");
[b]city.setRequired(true);[/b]
}
}
}
}
I am not a pro in Vaadin, I just started learning it. I am liking it so far.
I feel my code is hacky (specially the pieces of code in bold above), it just works somehow and probably i am not using the correct components. I feel there are better ways to do this simple task.
For instance I should be using FieldGroup Instead of FormLayout, But i was having trouble making it work.
Secondly I would like the validators to be automatically picked up from the anotations from my entity. But when I add a new Address to the person the Not Null validator is not fired for the text field city bound to the property on the beanitem.
Could someone please suggest me improvements and how to fix my code.
thanks in advance
Chahat
Hi, first off, if it's working that's already good considering you are starting with the framework :) Unfortunatelly, there are not bold lines in the code you provided (I think this forum doesn't suport it). I'd recomend you watch
With BeanFieldGroup, you can create any layout containing input components (such as text fields, check boxes, combo boxes, ...) and bind them to a POJO. For example:
public class Person {
private String firstame;
privete String lastName;
... getters and setters ...
}
public class PersonForm extends FormLayout {
private TextField firstName;
private TextField lastName;
... configure the componones and add them to the layout ...
}
BeanFieldGroup.bindFieldsUnbuffered(somePersonInstance, somePersonFormInstance);
About the not null validator, if you mean you want to use Java Bean Validation API, take a look at this example.
Happy coding! :)