What is the best solution to bind collections using the Binder class via Checkbox for example: Set userRoles = new HashSet<>();
I can do method to set roles without Binder like this:
You’re right. CheckboxGroup is available for Vaadin 10+, although it is not visible as an official component for Vaadin 14 on the site at the moment.
How do you got him set up Martin?
CheckboxGroup<RoleEntity> userRoles = new CheckboxGroup<>();
userRoles.setItems(Stream.of(admin,baker).collect(Collectors.toSet()));
binder.bind(userRoles,"userRoles");
???
In this configuratiuon it is still impossible to get value from database in CheckboxGroup after saving.
If you create your binding manually with binder.forField(userRoles).bind(some-read-method, some-write-method) your IDE will give you a compile time error if your types are not matching.
Thanks for the answer. I used a manual binding in Java 7 style, but I still have have a problem with visibility of the CheckBox selection in UserEditor. Other fields are filled with data when I click on object. What is wrong?
binder.bind(userRoles , new ValueProvider<UserEntity, Set<RoleEntity>>() {
@Override
public Set<RoleEntity> apply(UserEntity userEntity) {
Set<RoleEntity> listOfRoles = userEntity.getUserRoles();
return listOfRoles;
}},
new Setter<UserEntity, Set<RoleEntity> >(){
@Override
public void accept(UserEntity userEntity, Set<RoleEntity> roles){
userEntity.setUserRoles (roles);
}
});
Have you implemented the equals() method in your RoleEntity? If not, and you are using JPA/Spring Data/Hibernate etc., you should be aware that when you save and load an entity it will become new object in memory.
The checkboxgroup - as well as other components that uses a collection of objects - depends on the equals() method to determine if the objects are the same.
Consider this scenario:
// Create role
RoleEntity admin = new RoleEntity("Administrator");
admin = roleService.save(admin);
..
// create user that uses admin role
UserEntity john = new User("John", admin);
john = userService.save(john);
..
// Load john
john = userService.find(...);
if (john.hasRole(admin)) {
// do something
}
The above code will only work if the RoleEntity overrides the equals() method. If RoleEntity does not implement an equals, the CheckboxGroup will see the Items (the options) as being different from the values, even if they are both “admin” roles and for that reason the checkboxes will appear as unchecked when you load.
There are different ways you can construct equals and hashcode. At the most basic level, you should check that object and class matches and that Id is the same.
Example:
UUID roleId;
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((roleId == null) ? 0 : roleId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
Role other = (Role) obj;
if (roleId != null) return roleId.equals(other.roleId);
return false;
}
The addition of hashCode () and equals () implementations in the RoleEntity class solved my problem. Thank you so much for the professional explanation. Should I override these methods in every entity class?
AFAIK all flow components that deals with collections of data relies directly or indirectly on equals. This includes select, combobox, checkboxgroup, grid etc. So all entities that are used in these components will need equals to work.
Also, there are more sophisticated ways of checking equals and hashcode. Do a google search on java equals, specifically as it relates to entities. Some advocate checking all member fields for equality. For entitites, you may consider the objects as being equal if the ID/primary key is the same, regardless of other member fields.