Trying to love ComboBox

Because else I’ll have to create my own replacement, and I would really like to avoid that.

First is the behavior itself:
https://vaadin.com/docs/latest/components/combo-box

In 1st example:

  • Write “fin” and tab out → The field is reset to previous value instead of picking Finland
  • Write “fimland” and tab out → Since you wrote an non-existing value, it is reset to previous value

Both of these are by design, but I think it is poor design
Imho, when there is 1 filtered value left in the list, it should’ve picked it.
Also, the user should be notified that they have entered an invalid value. Silently replacing it is just wrong.

Looks like I can work around this by allowing custom values, listen to CustomValueSet, do my own filtering and either set the value or set to invalid.

Then performance:

  • With the country dropdown open, it takes around 1s between I type a char and the wheel stops spinning.

That might be explained by the server roundtrip, but that part seems to be snappy.
In fact, when you have a combobox with 50 or less entries, so that all are on the client-side, there is still a server-call but it doesn’t cause any noticeable delay.

Then there are the bugs:

  • If I write “finland” too quickly, it sometimes resets to previous value, or it leaves it as “finland” instead of replacing with “Finland”
  • In the Auto-open example, before I open the dropdown the first time, if I write “asd”, delete it and then open the dropdown, I get a blank dropdown with a spinning wheel in the top right corner.

These are just the things I found while playing around with it for half an hour.
Last time I played around with it I found other bugs, also to do with filtering.

So hard to love…

Thanks for the feedback, your points are pretty familiar to me and you are not alone. I have been wrestling with couple of them. In general I would say that ComboBox(es) is the second most complex component in the framework after Grid(s). There are so many competing behaviors that are hard to put in single component. My take on this has been that we should not have only one ComboBox, but many in order to scale to different needs.

Write “fin” and tab out → The field is reset to previous value instead of picking Finland

Regarding the first point you may be interested on this solution AutoSelectComboBox - Vaadin Add-on Directory

Looks like I can work around this by allowing custom values

This is likely to be possible, then you do it in server side. The add-on I linked above does it differently browser side.

With the country dropdown open, it takes around 1s between I type a char and the wheel stops spinning.

Yes, that is because the items are not fetched by the client before it is opened first time.

One proposal has been to have Light variant of the ComboBox, that does not have the lazy loading features and eagerly loads the items to client on attach. Such variant exists as an add-on

So hard to love…

Yes, it is tough love sometimes.

@yummy-rhino , thanks for the reply. I’ll check out your two suggestions.
The first one sounds like it is exactly what I need. The 2nd one not; I do want lazy-load, since all my dropdowns are populated from the db, but I don’t need pagination, since we don’t use dropdowns for large datasets. (We have “popup” for that)

I guess my main issue with ComboBox is that I don’t trust it. Maybe I can work around the issues I’ve found, but I found more today, so I have to assume there are even more… I feel uneasy about going to production with it :frowning_with_open_mouth:

I can understand why Grid is complex. Looking at the internal structure with the browser’s devtools, I am amazed that it works at all.
ComboBox, as a concept, seems like it should be trivial… I wonder if the problem is a “client-side first” mindset dictated by WebComponents.
To me, client-side only has value as long as it serves the server-side. There is no value to me that the ComboBox is a self-contained widget on the client-side. All I need is that it is a self-contained thing on the server-side…
I’m starting to wonder if WebComponents is another one of those too-brilliant ideas, like GWT.

Looked at HTMX? :slightly_smiling_face:

seems like it should be trivial…
Yes, it is decepting. The combination of 1. filtering, 2. lazy loading, 3. possibility to add items on the fly is actually very complex to keep client and server states in sync. When you draw the state machine on paper, you will notice it.

Yep. If your use case is just a simple selection with a short list of items, a NativeSelect may be a better choice than a ComboBox. But the problem is, you probably want more features than what the <select> element can provide, even if you discount lazy loading. There’s no way for the user to filter the results. You can’t really style the items, or make them contain anything else except text. Since it’s a browser-native element, there’s very little you can do about it. ComboBox doesn’t cover all of the possible use cases of selecting a single value off a list, either, but I’d like to think it covers some more

And I agree with Tatu with there being a need for more than one component that looks somewhat same but behaves differently; a ComboBox is not an Autofill component, for example.

@vital-koala, even if many of our dropdowns have few values (and none have insane no of rows), they all come from the db. I don’t want to display a form and immediately run 10 individual queries to get the first items for each ComboBox.
And, as you say, I still want most of the (good) behaviour of the ComboBox. For instance, I’m really looking forwards to testing out the multiselect.
So, a lazy-loading ComboBox seems like the best fit.

What I think I want, is a textfield and an anchored panel, both of which I control from the server side. Then I’ll put a grid in the panel, and filter it on the server-side when I get a valuechanged from the textfield.
The client-side open/closes the panel with the dropdown button, and moves down into the panel with arrow-down, but apart from that it is dumb.
That is more or less what we hacked together in our Vaadin8 application.
I’ve come to appreciate the ComboBox renderer, but I still miss having multiple columns.

To ease the migration I have created something like that, but it is really janky; Mostly because the “dropdown” is shown in a centered Dialog.
Maybe this will become a better solution when we can anchor divs relative to other divs. I understand that is on its way to the browsers?

<dialog>: The Dialog element - HTML: HyperText Markup Language | MDN is at least a new-ish feature that can be useful, yes

popover - HTML: HyperText Markup Language | MDN is another thing, less well supported

What I think I want, is a textfield and an anchored panel, both of which I control from the server side. Then I’ll put a grid in the panel, and filter it on the server-side when I get a valuechanged from the textfield.

There is an add-on that does exactly that, but not with Grid but TreeGrid here. I would recommend to check the source code and replace Tree with Grid in order to achieve the same behavior.

@yummy-rhino , Great! And so little code to achieve it too. I’ll try it out right away.
I might still want to investigate ComboBox for its multiselect, but this one seems like it should be an 1:1 match with our current requirements :slightly_smiling_face:

@yummy-rhino, a quick and dirty test worked like a charm:


    var textField = new TextField(title);
    var icon = VaadinIcon.ANGLE_DOWN.create();
    var popup = new com.vaadin.componentfactory.Popup();

    textField.setPrefixComponent(popup);
    textField.setSuffixComponent(icon);

    var id = "popup-opener-" + sequence++;

    textField.setId(id);
    popup.setFor(id);

    Grid<Person> content = new Grid<>(Person.class);
    content.setMinWidth("300px");
    content.setItems(persons);
    content.addColumn(Person::lastName).setHeader("Last Name");
    content.addColumn(Person::firstName).setHeader("First Name");

    popup.add(content);

    add(textField);

}```

Setting my current "dropdown" as content doesn't work quite so well; Looks like I'm fighting the the auto-sizing componentfactory.Popup does.  Instead of displaying above the field at the bottom of the page, it shows in a small box with scrollbars below the field. Since the simple case above works, I assume I'll get around this.

One sad thing is that it is not possible to resize or move the Popup
It is still perfect for my "dropdowns", but is less suitable for my more complex "popups". Would be fun to show these anchored to the input field, but they work fine in a centered Dialog as well
![image.png|335x474](upload://9PUDffUkrWyGXLAzLDzwwGiBrKd.png)

Also take a look into Lookup Field - Vaadin Add-on Directory if you haven’t already

@vital-koala, thanks for the suggestion. Must confess I haven’t used the directory much. Maybe I should.
That particular component seems to be more like our complex “popups”. For these we already have an acceptable solution in place; A textfield with an […] icon in the suffix, that opens a centered Dialog. The rest is just simple server-side code.
Since most of them are big, it doesn’t make so much sense to anchor them to the input field, so we avoid all the problems we have with “dropdown”
image.png

I’ve now come as far as I think I can with my ComboBox replacement, using a regular TextField, an anchored componentfactory.Popup and a Grid.
It is almost great. The only thing I struggle with is controlling when the Popup opens/closes. There are limits to how much I can do using blurListener on the server-side.
For instance: I open the Popup manually. This also means I have to close it manually when user tabs out of the textfield. I do this with a blurListener.
However, if the popup is open and the textfield has focus, if I click on the scroll handlers in the Grid in the Popup, the blurListener is also called, and I close the Popup…
I don’t think I can tell the difference between these two cases.
So, just almost great
image.png

However, if the popup is open and the textfield has focus, if I click on the scroll handlers in the Grid in the Popup, the blurListener is also called
Yes, that is the correct behavior, you focused the Grid so TextField is blured. Grid does not have functioning programmatic focus as there is no final decision how it actually should behave. My assumption is that it should focus the first cell on the first row. And I have added such implementation in the Tree component I use in my TreeComboBox add-on. See here: tree-flow/src/main/java/org/vaadin/tatu/Tree.java at v24 · TatuLund/tree-flow · GitHub By adding that to yours, you can programmatically focus the Grid already when Popup is opened, like here: tree-combo-box/src/main/java/com/vaadin/componentfactory/TreeComboBox.java at v24 · vaadin-component-factory/tree-combo-box · GitHub and then again return it to TextField when selection done like here: tree-combo-box/src/main/java/com/vaadin/componentfactory/TreeComboBox.java at v24 · vaadin-component-factory/tree-combo-box · GitHub

@yummy-rhino, yes I found that when I discovered grid.focus did nothing and googled it, so that part is ok.
I set 1st arrow-down to just open the Popup, so that I can continue to write in filter, and 2nd arrow-down to move to 1st row in grid.
Remaining issue has to do with how to keep the Popup open when I want it to be open, and close it when I want it closed.