Nested Collections + Forms

Hello.

I have a difficulty with displaying (editing) fields of my instance of a class A in a form in this way http://demo.vaadin.com/sampler#FormBasic

The class A besides other properties has ArrayList property which consists of a several objects. These objects are instances of a class which embrace two other objects like this:
class A has “ArrayList commonParameters”
class CommonParameter has CommonParameterName (name, type properties) and CommonParameterValue (value property).

CommonParameter (CommonParameterName+CommonParameterValue) is made to ease the use of the names/values.

So firstly i create a BeanItem of a class A and a form with its source of that BeanItem so that i am able to provide the properties on a page in a form.
I need also to make another form (CommonParameter class) for displaying name, type (CommonParameterName) and value (CommonParameterValue) in another form on this page, but as i understand i can get those properties (using BeanItem) only if i

  • know the name
  • have a gettter/setter for the name

However that properties are taken from a reference book which is filled by the users (list of CommonParameter). So i cannot really say how many items are there, their names and values.

Could you please help me to solve this puzzle.

p.s. I read many threads on the issue but wasn’t able to find the answer. I know that a CustomField addon might be useful in such a case and i’m going to check it now.

Up.

Hi,

Did you have problems with CustomField? You should be able to create an editor for the list containing the CommonParameters using CustomField (which is basically a CustomLayout that implements the Field interface), which can then be added to your form.

Please be more specific about your problems with CustomField if you still haven’t been able to get it working.

HTH,
/Jonatan

Hello Jonatan.

Thank you for a reply.

I’ve made some pictures to clarify an issue. I’ve made them quickly and by the phone’s camera so don’t judge me =O )

What i need to do is a page with several forms. One (at top - 1) shows the properties (contained by the object itself or it’s relatives) of a “main” object in domain model, the other forms (each one) reflect the state of several objects or an object having a several objects whatsoever.

Thus the simple use case here is a form 3 that shows strings | Name value | vertically located. Each string represents an object from myList which is held by the main object. I need to print that in a single form keeping in mind that each of the forms will be edited later and that changes must be handled to persist them, which should be quite simple with this form–beans mechanism.

Hope i’ve not confused you more with the elaboration.

p.s. i’ve tried to understand the way i should act by reading all example files of CustomField but i’ve failed.
11600.jpg
11601.jpg
11602.jpg
11603.jpg

Ok, I’ll try to give you a few pointers as to how CustomField works.

Think of CustomField as a layout, that you can build in any way you like. In this case you probably want to build the layout so that it contains N pairs of textfields (name and value), where N is the size of the list containing your data.

You can build this layout by looping over every item in the list you pass in to CustomField (by the setValue() method) and adding two fields for each. You can bind these fields to their data objects using e.g. ObjectProperty and the values stored in the list will be automatically updated when you call commit() on the fields. Or they can be write through depending on whether you turn on buffering in the text fields or not, which you probably want to do.

Keep a reference to all text fields somewhere, e.g. in a list.

CustomField provides a value variable in the class and default implementations for getValue() and setValue(). This means that updating value will do The Right Thing™ by default. You may want to override CustomField.commit() to call commit() on all your textfields (or store the edited values in the list, i.e. the value variable, in some other way).

Hope this gives you some hints as to get started. See the
source code
for the CustomField demos for concrete code examples.

/Jonatan

Hello.

Thank you for your help. However code in CustomField is a bit complicated for me.
Could you please provide additional details on “looping over every item in the list you pass in to CustomField (by the setValue() method) and adding two fields for each. You can bind these fields to their data objects using e.g. ObjectProperty” with a code hint?

I was able to manage showing items this way, however i cannot save the fields. There’s no updating on commit action.


		cpItem = new BeanItem<CommonParameter>(new CommonParameter());
		
		List<String> prs = new ArrayList<String>(5);
		for (CommonParameter cp : result.getCommonParametersList()) {
			cpItem.addItemProperty(cp.getName(), new ObjectProperty<Object>(cp.getValue()));
			prs.add(cp.getName());
		}
...
		cpForm.setFormFieldFactory(new CommonParameterFieldFactory(state));
		cpForm.setItemDataSource(cpItem);
                cpForm.setVisibleItemProperties(prs);

Up

I am having the same issue. I have a similar data model to the one that is there in the example for CustomField. My domain model consists of a parent entity called the “OrgUnit” which contains an “Adress” entity. I followed the example “NestedPersonForm” provided with the CustomField plugin and the display works fine, but, the commit does not. No exception is thrown either.

Any thoughts or ideas appreciated.

Thanks in advance,
Kishore

Hi,

A simple, completely untested, sample pseudo-code would be:

public class CommonParameterListField extends CustomField {
    private List<TextField> fields;
    .
    .
    .
    @Override
    public void setValue(Object value) {
        super.setValue(value);
        // value is a List<CommonParameter>
        List<CommonParameter> params = (List<CommonParameter>)value;
        layout.removeAllComponents();
        properties.clear();
        for(CommonParameter p : params) {
            TextField pf = new TextField(p.getName(), new MethodProperty(p, "value"));
            layout.addComponent(pf);
            fields.add(pf);
        }
    }

    @Override
    public void commit() {
        super.commit();
        for(TextField f : fields) {
            f.commit():
        }
    }
}

… Or something along those lines.

I didn’t really understand the context of your code-snippet. Was it in your CustomField, or in your FieldFactory?

HTH,
/Jonatan

Hello

I was also facing the same problem for couple of days (I was able to show the fields, however I cannot save the fields).
Finally I found the solution. The most important thing is to (in your CustomField) override methods: setInternalValue, getInternalValue.

Br
Marcin

Hi all,

I’ve had some trouble getting this to work using FieldGroup, CustomField and BeanItemContainer in Vaadin7. Solution was to override setPropertyDataSource and getPropertyDataSource like so:

import java.util.ArrayList;
import java.util.List;

import com.vaadin.data.Property;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomField;
import com.vaadin.ui.VerticalLayout;

public class CustomFieldCollectionsExample extends CustomField {
    // reference to original datasource
    private Property datasource;
    private BeanItemContainer<Object> beanContainer = new BeanItemContainer<Object>(
            Object.class);
    private VerticalLayout lay = new VerticalLayout();

    @Override
    protected Component initContent() {
        // TODO create CRUD for editing object beans
        return lay;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    // called by FieldGroup.bind(), FieldGroup.setItemDataSource() etc.
    public void setPropertyDataSource(Property newDataSource) {
        Object value = newDataSource.getValue();
        if (value instanceof List<?>) {
            // synch newDataSource - beanItemContainer
            List<Object> beans = (List<Object>) value;
            beanContainer.removeAllItems();
            beanContainer.addAll(beans);

        } else {
            return;
        }

        // update reference
        datasource = newDataSource;
        super.setPropertyDataSource(newDataSource);
    }

    @SuppressWarnings("unchecked")
    @Override
    // called by FieldGroup.commit() etc.
    public Property getPropertyDataSource() {
        // copy beans
        List<Object> beans = new ArrayList<Object>();
        for (Object itemId : beanContainer.getItemIds()) {
            beans.add(beanContainer.getItem(itemId).getBean());
        }
        // Updates AbstractField internal value. Make sure to have getters and
        // setters for all fields in POJOS to avoid Property.ReadOnlyException()
        setInternalValue(beans);
        // or super.getPropertyDataSource()
        return datasource;

    }

    @Override
    public Class<List> getType() {
        return List.class;
    }

}

edit 2014/10/28: replaced call in getPropertyDataSource() to setValue() with setInternalValue() to avoid infinite loop (AbstractField.setValue calls AbstractField.getPropertyDataSource)