V8 ComboBox value set

I have two related (bound) comboboxes, A and B. A has a ValueChangeListener that fetches the items and sets the default for B. The problem is that if the item list for B is empty and I try to setValue(null) in order to reset the previous value it still sticks around and is visible in B. B allows null values and I tried various other setSelected/reset on B but the previous selection is still not cleared. Any pointers?

It would be helpful if you shared some code, particularly the classes that you are using as the generic types for A and B.

In any case, I’ve worked up a simple example that shows how to use V8 ComboBox with a basic String as well as a custom class. In short, if you have a custom class for B, you should create an instance that returns the empty string from its
toString()
method, or whatever method you may have passed to
.setItemCaptionGenerator()
on ComboxBox B. If you are using String as the generic type for B, just pass the empty string.

public class MyUI extends UI {
    private static class StringWrapper {
        private final String string;
        public StringWrapper(String string) { this.string = string; }
        @Override public String toString() { return string; }
        public static final StringWrapper EMPTY = new StringWrapper("");
    }

    private final HashMap<String, List<String>> data1 =
        new HashMap<String, List<String>>() {{
            put("A", Arrays.asList("A1", "A2", "A3"));
            put("B", Arrays.asList("B1", "B2", "B3"));
            put("C", Arrays.asList("C1", "C2", "C3"));
            put("D", Arrays.asList());
        }};

    private final HashMap<String, List<StringWrapper>> data2 =
        new HashMap<String, List<StringWrapper>>() {{
            put("A", Arrays.asList(new StringWrapper("A1"), new StringWrapper("A2"), new StringWrapper("A3")));
            put("B", Arrays.asList(new StringWrapper("B1"), new StringWrapper("B2"), new StringWrapper("B3")));
            put("C", Arrays.asList(new StringWrapper("C1"), new StringWrapper("C2"), new StringWrapper("C3")));
            put("D", Arrays.asList());
        }};

    @Override protected void init(VaadinRequest vaadinRequest) {
        final HorizontalLayout boxLayout = new HorizontalLayout();
        setContent(boxLayout);

        final ComboBox<String> box1 = new ComboBox<String>();
        final ComboBox<String> box2 = new ComboBox<String>();
        final ComboBox<StringWrapper> box3 = new ComboBox<StringWrapper>();
        boxLayout.addComponents(box1, box2, box3);

        box1.addValueChangeListener(event -> {
            // Swap out values for box2 based on what's selected in box1
            List<String> items = data1.get(box1.getValue());
            box2.setItems(items);
            box2.setSelectedItem(items.stream().findFirst().orElse(""));
        });

        box1.addValueChangeListener(event -> {
            /*
             * Swap out values for box3 based on what's selected in box1. Here we
             * use a custom class and a static instance whose toString() returns
             * the empty string.
             */
            List<StringWrapper> items = data2.get(box1.getValue());
            box3.setItems(items);
            box3.setSelectedItem(items.stream().findFirst().orElse(StringWrapper.EMPTY));
        });

        Set<String> items = data1.keySet();
        box1.setItems(items);
        box1.setSelectedItem(items.stream().findFirst().get());
        box1.setEmptySelectionAllowed(false);
    }

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

Ah, I think I see the problem now - thanks for your time. There is no longer some sort of
setNullSelectionItemId in V8?

Nicklas,

That is correct:
setNullSelectionItemId()
came from the
AbstractSelect
class, which has been replaced by the
AbstractSingleSelect
class. Data binding in V8 was completely overhauled to use generics for type safety and better run-time performance. That is why
ComboBox
is now
ComboBox
, as with other selection components. In V8, you can specify which method on your class T returns a caption string to be displayed by the selection component by using
.setItemCaptionGenerator()
. The default is simply to call
.toString()
on the object. Thus, you can get the same effect by simply having an object that returns the empty string from its caption method, without having to explicitly use null.

This approach is not necessarily the only – or best – way to do it, but it’s the most similar to the setNull… approach from V7, in which you explicitly designate an object to mean “empty”. Take a look at the
HasValue
interface (
docs
), which is implemented by selection components. In particular, look at the
getOptionalValue()
method, which uses
java.util.Optional
.

– AC

Kind of interesting that there is a


https://vaadin.com/api/8.0.4/com/vaadin/data/HasValue.html#getEmptyValue–

I don’t remember for sure but I have a feeling that having a null object with the “”-toString also interfers with a required-validation-check so that it’s actually considered a value(?)

Hey, the binder has a withNullRepresentation that looks promising!