I just
filed a bug report
but I wanted to let people know here in case they encounter this issue and search this forum because I just spent 2-3 days trying to figure out what was wrong with my code only to discover it appears to be a bug within Vaadin’s framework. Don’t get me wrong, Vaadin is a GREAT framework and I strongly recommend it, but like every piece of software there are bugs, and hopefully it will be resolved very soon because it’s kinda critical for anyone who sorts their data other than sequential.
This affects versions of Vaadin up to at least 8.1.2.
In the example below if the list of Cars is sorted according to the ID’s then the ComboBox works as expected. However if I randomly shuffle the list of Cars then the combobox will randomly appear on the screen unselected (it’s actually not really random but this is discussed later).
final VerticalLayout layout = new VerticalLayout();
List<Car> cars = Arrays.asList(
new Car(1, "Honda1"),
new Car(2, "Honda2"),
new Car(3, "Honda3"),
new Car(4, "Honda4"),
new Car(5, "Honda5"),
new Car(6, "Honda6"),
new Car(7, "Honda7"),
new Car(8, "Honda8"),
new Car(9, "Honda9"),
new Car(10, "Honda10"),
new Car(11, "Honda11")
);
// The shuffle will cause the ComboBox to be unselected randomly
Collections.shuffle(cars);
Person person = new Person("BuyerOfCar3", 3);
ComboBox<Car> carComboBox = new ComboBox<Car>();
carComboBox.setItemCaptionGenerator(Car::getName);
carComboBox.setItems(cars);
Binder<Person> binder = new Binder<Person>();
binder.forField(carComboBox)
.withConverter(new ComboBoxCarToIdConverter())
.bind(Person::getCarID, Person::setCarID);
binder.readBean(person);
layout.addComponents(carComboBox);
setContent(layout);
In the above code the line: Collections.shuffle(cars); will randomly cause the ComboBox to be unselected. If the list is sorted by ID then there is no issue however as soon as I randomly shuffle the list the ComboBox will randomly fail. In fact it’s not randomly fail but rather it seems to search until the value is greater than (or something similar) and will stop checking in the list after that. In other words the Binder logic seems to assume the list is sorted in some fashion and will therefore fail for lists sorted in non-expected ways. In my real code I sort the list based on two criteria, one of which is the data’s status (for example if the car is sold or not), and hence the ID’s appear non-sequentially.
The ComboBoxCarToIdConverter is:
class ComboBoxCarToIdConverter implements Converter<Car, Integer>
{
@Override
public Result<Integer> convertToModel(Car car, ValueContext context)
{
if(car == null)
return Result.ok(-1);
return Result.ok(car.getId());
}
@Override
public Car convertToPresentation(Integer id, ValueContext context)
{
if(id > -1)
return new Car(id);
return null;
}
}
And the Data objects are:
public class Person
{
private String name;
private int carID;
public Person(String name, int carID)
{
this.name = name;
this.carID = carID;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getCarID()
{
return carID;
}
public void setCarID(int carID)
{
this.carID = carID;
}
}
And:
public class Car
{
private int id;
private String name;
public Car(int id)
{
this.id = id;
}
public Car(int id, String name)
{
this.id = id;
this.name = name;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Override
public int hashCode()
{
return getId();
}
@Override
public boolean equals(Object obj)
{
if(this == obj)
return true;
if(obj == null || getClass() != obj.getClass())
return false;
return getId() == ((Car)obj).getId();
}
}