Bind an arraylist of Strings

Hello,
Migrating from Vaadin 7-> 8 I’ve decided to start using the binder interface and I have the following question.

Suppose we have an object

public class Person {
 
 private String name = "";
 private ArrayList<String> companies = new ArrayList<String>();

etc...
}

by using the binder I can bind the normal fields such as boolean/text etc using:
binder.forField(nameTextField).bind(Person::getName, Person::setName);

but the question is HOW to bind the

ArrayList<String> to a Grid<String>

??

Is there a way or the binder just doesn’t support nested objects?

Thanks!
Nick

Hi, do you already have a solution for this issue??

I have exactly the same problem :smiley:

thank you Paul

Nope no solution so far…
I just have to use the binder for all the other fields and before saving I have to ‘manually’ check the entire list and put it at the object…
If anyone has any idea please be my guest!! :slight_smile:

I made a quick hack which seems to work for the simple use case of one way (read only) binding a List<String> property of a Bean to a custom Composite (which in turn is really just a Grid); here’s the full runnable source code:

package org.vaadin;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.Binder;
import com.vaadin.data.HasValue;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.shared.Registration;
import com.vaadin.ui.*;

import javax.servlet.annotation.WebServlet;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

@Theme("mytheme")
public class MyUI extends UI {

    public static class StringList extends Composite implements HasValue<List<String>> {

        private List<String> strings = null;
        Grid.Column<String, String> column;
        Grid<String> grid;

        public StringList() {
            grid = new Grid<>();
            column = grid.addColumn(s -> s);
            setCompositionRoot(grid);
        }

        @Override
        public void setValue(List<String> strings) {
            this.strings = strings;
            grid.setItems(strings);
        }

        public void setCaption(String caption) {
            column.setCaption(caption);
        }

        @Override
        public List<String> getValue() {
            return strings;
        }

        @Override
        public Registration addValueChangeListener(ValueChangeListener<List<String>> valueChangeListener) {
            return null;
        }

        @Override
        public void setRequiredIndicatorVisible(boolean b) {
            super.setRequiredIndicatorVisible(b);
        }

        @Override
        public boolean isRequiredIndicatorVisible() {
            return super.isRequiredIndicatorVisible();
        }

        @Override
        public void setReadOnly(boolean readOnly) {
            super.setReadOnly(readOnly);
        }

        @Override
        public boolean isReadOnly() {
            return super.isReadOnly();
        }
    }

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        final VerticalLayout layout = new VerticalLayout();

        Person person = new Person();
        person.setName("Name");
        person.setDayOfBirth(LocalDate.of(2010,5,23));
        person.setFruits(Arrays.asList("banana", "apple", "potato"));


        TextField nameTextField = new TextField("Name");
        DateField dateField = new DateField("Day of birth");
        StringList stringList = new StringList();
        stringList.setCaption("Fruits");

        layout.addComponents(nameTextField, dateField, stringList);

        Binder<Person> personBinder = new Binder<>();
        personBinder.forField(nameTextField).bind(Person::getName, Person::setName);
        personBinder.forField(dateField).bind(Person::getDayOfBirth, Person::setDayOfBirth);
        personBinder.forField(stringList).bind(Person::getFruits, Person::setFruits);

        personBinder.readBean(person);



        setContent(layout);
    }

    public static class Person {
        private String name;

        private LocalDate dayOfBirth;

        private List<String> fruits;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public List<String> getFruits() {
            return fruits;
        }

        public void setFruits(List<String> fruits) {
            this.fruits = fruits;
        }

        public LocalDate getDayOfBirth() {
            return dayOfBirth;
        }

        public void setDayOfBirth(LocalDate dayOfBirth) {
            this.dayOfBirth = dayOfBirth;
        }
    }

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet {
    }
}

I’m gonna add a little explanation, supporting Ollis code example:

For any kind of list or set, even if it’s “just” a list of Strings, you will have to use Grids. By utilizing the grid’s editor, the user is able to see and edit any item in that list. The items shown in the grid are NOT copies of the bound bean’s list so any change in the grid will directly change the items of the bound bean.

You could even define your own grid editor save listener if you wanted to save these items in a DB independently of the bound bean, but I recommend only doing this if proper [cascading]
(https://vladmihalcea.com/a-beginners-guide-to-jpa-and-hibernate-cascade-types/) is not possible.

Edit: With ollis example of the StringList class, the grid editor is not used, therefore the user will not be able to edit any of the shown Strings. But what he did is implementing HasValue which will enable it to set up the binding in the same way any other binding is done - with binder.forField(field).bind(getter, setter); If you enable the grid editor in Ollis example (only if you need to edit the strings there of course), it will be the perfect solution in my opinion.