For example in the Select
component the selected value is rendered as shown here. However when it comes to the ComboBox
it is not rendered, only on the dropdown as shown here. I need to use the ComboBox
because I need the search functionality, that is to have the item selected as they type in the value because there may be a lot of values. Ideally it would be great to merge the Select
and ComboBox
but barring that I'm wondering if there's a way to render the selected value.
Asked
Active
Viewed 1,274 times
3

Stephane Grenier
- 15,527
- 38
- 117
- 192
-
How would filtering work if there wouldn't be an embedded text field with a text representation of the selected value? – Leif Åstrand Sep 25 '21 at 05:54
-
The same way the rendered value is displayed when not selected. – Stephane Grenier Sep 27 '21 at 19:31
-
1ComboBox needs to use a text field in that location for filtering to work (unless we're talking about a completely different UI design for the component). A text field can only render text (without making lots of other things complicated), which would make quite difficult to render the item in the same way. It seems like you're wishing for a different kind of component which isn't part of Vaadin. – Leif Åstrand Sep 28 '21 at 11:21
-
That makes sense but then how was it done in Vaadin 8? I'm basically upgrading a vaadin 8 app to the latest version of vaadin and that functionality seems to have disappeared. – Stephane Grenier Sep 28 '21 at 20:38
-
1The ComboBox in Vaadin 8 only supports item icons in addition to a plain item label. The ComboBox in newer Vaadin versions supports arbitrary item rendering in the dropdown, such as the example you linked to with multiple lines of text and varying fonts. It might be possible to somehow inject an icon as a prefix for the text field, but that would be quite custom solution. – Leif Åstrand Sep 29 '21 at 05:49
-
That makes a lot sense. Thanks for the detailed information. – Stephane Grenier Oct 04 '21 at 20:12
1 Answers
6
You can't use an arbitrary Renderer, because the text input is, well, a text input. As noted in the comments below the question, what you're really after is an icon in front of the value of the input, and while there's no nice API in ComboBox for this, you can frankenstein together a solution using the prefix slot of the vaadin-text-field
input. I've adapted an example using the Cookbook recipe here. Note that there's an enhancement request that would make handling prefix/suffix components in ComboBox easier: https://github.com/vaadin/flow-components/issues/1594
public class AboutView extends Div {
public AboutView() {
ComboBox<Person> comboBox = new ComboBox<>();
comboBox.setItems(getPersons());
// Renderer for the drop down
comboBox.setRenderer(new ComponentRenderer<Div, Person>(person -> {
Div container = new Div();
container.add(person.getIcon().create(), new Span(person.getName()));
return container;
}));
// on value change: either clear the prefix slot or create a new Icon there
comboBox.addValueChangeListener(e -> {
Person p = e.getValue();
if (p == null) {
PrefixUtil.clearSlot(comboBox, "prefix");
return;
}
PrefixUtil.setPrefixComponent(comboBox, p.getIcon().create());
});
comboBox.setItemLabelGenerator(Person::getName);
add(comboBox);
}
public List<Person> getPersons() {
List<Person> persons = new ArrayList<>();
Person person1 = new Person("Foo", VaadinIcon.ARROW_BACKWARD);
Person person2 = new Person("Bar", VaadinIcon.BAR_CHART);
Person person3 = new Person("Baz", VaadinIcon.PUZZLE_PIECE);
persons.add(person1);
persons.add(person2);
persons.add(person3);
return persons;
}
public static class Person {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
public VaadinIcon getIcon() {
return icon;
}
public void setIcon(VaadinIcon icon) {
this.icon = icon;
}
private VaadinIcon icon;
public Person(String name, VaadinIcon icon) {
this.name = name;
this.icon = icon;
}
}
public static class PrefixUtil {
private static Stream<Element> getElementsInSlot(HasElement target,
String slot) {
return target.getElement().getChildren()
.filter(child -> slot.equals(child.getAttribute("slot")));
}
public static void setPrefixComponent(Component target, Component component) {
clearSlot(target, "prefix");
if (component != null) {
component.getElement().setAttribute("slot", "prefix");
target.getElement().appendChild(component.getElement());
}
}
private static void clearSlot(Component target, String slot) {
getElementsInSlot(target, slot).collect(Collectors.toList())
.forEach(target.getElement()::removeChild);
}
private static Component getChildInSlot(HasElement target, String slot) {
Optional<Element> element = getElementsInSlot(target, slot).findFirst();
if (element.isPresent()) {
return element.get().getComponent().get();
}
return null;
}
public static Component getPrefixComponent(Component target) {
return getChildInSlot(target, "prefix");
}
}
}

ollitietavainen
- 3,900
- 13
- 30