How to bind list of TextField to binder(FieldGroup or BeanFieldGroup)?

I have requirement like following.

In page for Employee record entree there are 3 types of field Name, Phone Numbers and Address. User can enter 1 name and 1 address per employee but he can add multiple phone numbers by using multiple text field.

Initially there will be only 1 text field for phone number but user can add more text field using button. So text fields for phone numbers can increase and decrease.

For this requirement it was easy to bind name and address, since there will only 1 entry. But for phone numbers I have to use list and I am unable to bind list of text fields to list of strings.

I think you’d need to make a CustomField<List> or something like that.

JPAContainer has a MasterDetailEditor for that purpose, as described in
the book chapter
, but it only works with JPA entities. You could see how it’s implemented to get some help, although the implementation may have complexity that is irrelevant to your case.

I tried this approach. While binding my custom field to field group I am facing problem.
FieldGroup fieldGroup = new BeanFieldGroup(List.class); // Is this correct??
fieldGroup.bind(myList.get(0), “0”); // How can I bind this??

No. It’s a bit more complex than that.

For example:

public class Planet implements Serializable {
    String name;
    List<String> moons = new ArrayList<String>();
    
    public Planet(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<String> getMoons() {
        return moons;
    }
    public void setMoons(List<String> moons) {
        this.moons = moons;
    }
}

// Simple editor for string lists
class ListEditor extends CustomField<List<String>> {
    VerticalLayout fields = new VerticalLayout();
    
    public ListEditor(String caption) {
        setCaption(caption);
    }

    @Override
    protected Component initContent() {
        HorizontalLayout content = new HorizontalLayout();
        content.addComponent(fields);
        
        Button add = new Button("+", this::addItem); // Java 8
        content.addComponent(add);
        content.setComponentAlignment(fields, Alignment.TOP_LEFT);
        content.setComponentAlignment(add, Alignment.BOTTOM_LEFT);
        return content;
    }
    
    void addItem(ClickEvent event) {
        List<String> list = getValue();
        if (list == null)
            list = new ArrayList<String>();
        list.add("");
        setValue(list);

        final TextField tf = new TextField();
        tf.addValueChangeListener(valueChange -> { // Java 8
            int index = fields.getComponentIndex(tf);
            List<String> values = getValue();
            values.set(index, tf.getValue());
            setValue(values);
        });
        fields.addComponent(tf);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public Class<? extends List<String>> getType() {
        return (Class<List<String>>) (new ArrayList<String>()).getClass();
    }
    
    @Override
    public List<String> getValue() {
        return super.getValue();
    }
}

// Define a form as a class that extends some layout
class MyForm extends VerticalLayout {
    // Member that will bind to the "name" property
    private TextField name = new TextField("Name");
    
    // Member that will bind to the "age" property
    private ListEditor moons = new ListEditor("Moons");

    private FieldGroup binder = new FieldGroup();
    
    private Runnable notifyOk;

    public MyForm(Item item, Runnable notifyOk) {
        setCaption("Planet");
        this.notifyOk = notifyOk;

        FormLayout form = new FormLayout();
        form.addComponents(name, moons);
        addComponent(form);
        
        addComponent(new Button("Commit", this::commit));

        // Now bind the member fields to the item
        binder.setItemDataSource(item);
        binder.bindMemberFields(this);
    }
    
    void commit(ClickEvent event) {
        try {
            binder.commit();
            notifyOk.run();
        } catch (CommitException e) {
            Notification.show("Commit failed");
        }
    }
}

// Here we use it
void onetomany(final VerticalLayout layout) {
    Planet planet = new Planet("My Planet");
    BeanItem<Planet> item = new BeanItem<Planet>(planet);
    layout.addComponent(new MyForm(item, () -> { // Java 8
        Notification.show(planet.name,
            planet.moons.toString(),
            Notification.Type.HUMANIZED_MESSAGE);
    }));
}

Thank you for the example!
The method getType() in line 62 does not compile in Java 7.
Netbeans says:
“incompatible types: Class<CAP#1> cannot be converted to Class<List> where CAP#1 is a fresh type-variable:
CAP#1 extends ArrayList from capture of ? extends ArrayList”
Any ideas how to define this method in Java 7?

Thank you.
17757.png

How can we do this with Vaadin 8 i.e. BeanValidationBinder?

The thread name is very misleading for the moment, since Vaadin 8 is released. Vaadin 8 has completely different data binding approach, and, unfortunately, uses class named
Binder.
So, for Vaadin 8 it’s better to start another thread. I think, for the moment the easiest solution is to recreate Binder if number of pnone numbers is changed.

I have already created another thread:
https://vaadin.com/forum#!/thread/16128692