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.
How to attach more than one bean in a form
Hi there,
I'm a newbie in vaadin. And due to my practice, I have to create a form to insert, update data for 2 table. Two table will look like this:
SUBJECT_TABLE
ID Name Description
1 MyName MyDescription
DETAILS_TABLE
ID | SubjectID | ParentID | Content
-----------------------------------------------
1 | 1 | Null | Content1
2 | Null | 1 | SubContent1.1
3 | Null | 1 | SubContent1.2
4 | 1 | Null | Content2
5 | Null | 4 | SubContent2.1
6 | Null | 4 | SubContent2.2
Are there anyway to input all of these data in only one form with bean attach? I can't find any thread that help me with this problem (or may be I have low ability of searching :( )
Please help or guide me to the right thread.
Thanks in advanced
TamNguyen
Hi Javier,
Thanks for your help but my problem is more complicated. T_T
Since I have to work with two table at a time, so the input form will look like the picture that I attached.
I already have two beans Subject and Details
@Entity
@Audited
@Table
public class Subject extends GeneratedKeyBaseEntity
{
private static final long serialVersionUID = 4563943969726213794L;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "description", nullable = false)
private String description;
@OneToOne(fetch = FetchType.LAZY, mappedBy = "subject")
private Details detail;
...
@Entity
@Audited
@Cacheable(true)
public class Details extends GeneratedKeyBaseEntity
{
private static final long serialVersionUID = 1413136371395313591L;
@NotAudited
@Column(name = "parentId")
private Long parentId;
@Column(name = "content", nullable = false)
private String content;
@OneToOne(fetch = FetchType.LAZY)
private Subject subject;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parentDetail")
private List<Details> subDetails;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parentId", insertable = false, updatable = false)
private Details parentDetail;
....
But I don't know how to put them in same form
String SUBJECT_NAME = "name";
String SUBJECT_DESCRIPTION = "description";
String DETAIL_CONTENT = "detail.content";
String[] PROPS = new String[]{SUBJECT_NAME, SUBJECT_DESCRIPTION, DETAIL_CONTENT};
subjectFrm = new Form()
{
private static final long serialVersionUID = -7468890841917310653L;
private FormLayout subjectFormLayout = new FormLayout();
{
this.setLayout(subjectFormLayout);
}
@Override
protected void attachField(final Object propertyId, final Field field)
{
if (SUBJECT_NAME.equals(propertyId))
{
subjectFormLayout.addComponent(field);
}
else if (SUBJECT_DESCRIPTION.equals(propertyId))
{
subjectFormLayout.addComponent(field);
}
else if (DETAIL_CONTENT.equals(propertyId))
{
subjectFormLayout.addComponent(field);
}
}
@Override
public void commit() throws SourceException, InvalidValueException
{
}
};
subjectFrm.setFormFieldFactory(new FormFieldFactory()
{
private static final long serialVersionUID = -5152824267335436654L;
@Override
public Field createField(final Item item, final Object propertyId, final Component uiContext)
{
Field field = null;
if (SUBJECT_NAME.equals(propertyId))
{
field = subjectNameTxt;
}
else if (SUBJECT_DESCRIPTION.equals(propertyId))
{
field = subjectDescriptionTxt;
}
else if (DETAIL_CONTENT.equals(propertyId))
{
field = detailContentTxt;
}
return field;
}
});
Subject subject = new Subject();
Details detail = new Details();
subject.setName("");
subject.setDescription("");
subject.setDetail(detail);
BeanItem<Subject> subjectBean = new BeanItem<Subject>(subject);
subjectFrm.setWriteThrough(false);
subjectFrm.setItemDataSource(subjectBean);
subjectFrm.setVisibleItemProperties(PROPS);
subjectFrm.setCaption("Subject");
subjectFrm.getFooter().addComponent(saveButton);
subjectFrm.getFooter().addComponent(discardButton);
Do you have any idea about this situation ?
Thanks and Best regards
Tam Nguyen
Tam Nguyen Minh: I already have two beans Subject and Details
public class Subject extends GeneratedKeyBaseEntity { @Column(name = "name", nullable = false) private String name; ... @OneToOne(fetch = FetchType.LAZY, mappedBy = "subject") private Details detail; ...
public class Details extends GeneratedKeyBaseEntity { ... @Column(name = "content", nullable = false) private String content; ....
But I don't know how to put them in same form
If you are using explicitly created BeanItems, the easiest solution is probably to use nested properties. Otherwise, check what the container you are using might provide - if I remember correctly, e.g. BeanContainer and JPAContainer support telling the container to automatically add specified nested properties to their items.
The code could look something like
myBeanItem.addItemProperty("content", new NestedMethodProperty(myBeanItem.getBean(), "detail.content"))
Vaadin 7 will also make adding nested properties directly to BeanItems a little easier with a helper method for the above.
EDIT: As you seem to be using JPA, make sure lazy loading does not cause problems - if necessary, force the loading of lazy fields. Depending on your use of the item, the JPA library used etc. this may or may not be a problem for you.
Hi Henri,
Thanks a lot. My problem is solved now. :D.
Before I mark this thread as Resolved, I would like to have one more question about bean with form. :)
Are there anyway (again :) ) to put a List of bean to a form?
For example:
I have three Details bean with id 1,2,3. Can I put these beans into only one form with first text area "Content" for content of Details bean1, second text area "Content" for content of Details bean 2, ...
I already know that we can display these bean using table
BeanItemContainer<Details> detailsBean = new BeanItemContainer<Details>(Details.class);
List<Details> detailsList = //code to get list of detail objects
detailsBean.addAll(detailsList );
Table table = new Table();
table.setContainerDataSource(detailsBean )
and thanks to Javier, I also know a trick to answer my question : using 3 sub forms to display 3 Details objects. But it would be cooler if we can go directly to one form with these beans, right ? :)
If you have time, please share your idea with us about this question.
And once again, thank you for helping me in previous problem.
Best regard,
Tam Nguyen
Hi Tam,
First of all, you don't have to thank nothing it's really a pleasure for me help other developers, also when my answer doesn't fit the needs of them :unsure:
And now, after read your post and review the code you post
Tam Nguyen Minh:
BeanItemContainer<Details> detailsBean = new BeanItemContainer<Details>(Details.class); List<Details> detailsList = //code to get list of detail objects detailsBean.addAll(detailsList ); Table table = new Table(); table.setContainerDataSource(detailsBean )
I think that the use of a table for your data structure is not bad, you can set editable mode on table and add a TableFieldFactory that shows the "content" property in bean as TextField (or TextArea as you prefer) and then save the changes you made on bean in table to your database, adding a Listener
There is a good example of how to implement this in the Book of Vaadin, and also here in the Book Examples Application you can view http://demo.vaadin.com/book-examples/book/#component.table.editable.
The code snippet could be similar to this one:
BeanItemContainer<Details> detailsBean = new BeanItemContainer<Details>(Details.class);
List<Details> detailsList = //code to get list of detail objects
detailsBean.addAll(detailsList );
Table table = new Table();
table.setContainerDataSource(detailsBean )
table.setTableFieldFactory(new DefaultFieldFactory() {
@Override
public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
// Create a TextField if the propertyId is content property of your Detail class
if ("content".equals(propertyId)) {
return new TextField();
}
// Otherwise use the default field factory
return super.createField(container, itemId, propertyId,uiContext);
}
});
// Add ValueChangeListener to listen changes on table data
table.addListener(new Property.ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
// Implement the logic to save your entity here
});
// Make table editable
table.setEditable(true)
Hope this helps, but if Henri has another idea, problably should be better than mine :)
Cheers,
Javi
Javier Serrano: I think that the use of a table for your data structure is not bad, you can set editable mode on table and add a TableFieldFactory that shows the "content" property in bean as TextField (or TextArea as you prefer) and then save the changes you made on bean in table to your database, adding a Listener
I agree that an editable Table is often a good approach in such situations, and can also be supplemented with controls to add/remove rows etc. if necessary. Note, though, that the value of a Table (also when on a Form) is the selected row id (or a Set of them for a multiselectable table), which can cause some complications if you put the table directly as a field in the form. You can get around this e.g. by wrapping the table in a CustomField and returning for instance a list of your beans or some other suitable representation - or by preventing the form from saving the value of that field and just using it for in-place editing.
Another option - in case you want the content fields of the sub-objects directly on the form - would be to add related properties by hand (e.g. looping over a list of sub-beans) to your BeanItem and then displaying the item on the Form. You could use MethodProperty, NestedMethodProperty or your own custom Property implementations (inheriting AbstractProperty) for this, and have a little helper class or a subclass of BeanItem to automate all this.
Hi Javier and Henri,
Thank you all for helping me about this problem. I will try both 2 solutions to improve my vaadin's knowlegde.
Hope someday I will have an opportunity to work with you two.
Thank you & best regards
Tam Nguyen