0

In my Vaadin application I have a dialog that creates a filter to search for books. As a result I am creating a Filter bean that looks like this:

public class Filter implements Serializable {

private Author author;
private String country;
private LocalDate postingDate;
private int fromYear;
private int toYear;
private Set<Tag> tags;
// setters and getters are omitted

}

And my dialog looks like this:

@Component
public class FilterPanel extends Dialog {

    private static final Logger LOG = LoggerFactory.getLogger(FilterPanel.class);

    private Filter filter;

    private ComboBox<Author> authorField;
    private ComboBox<String> countryField;
    private DatePicker postingDateField;
    private TextField fromYear;
    private TextField toYear;
    private TextField centuryField;
    private MultiSelectComboBox<Tag> tagField;
    private Button doneButton;

    private Binder<Filter> binder;

    @Autowired
    ApiBookUtils api;
    ...
   }

I have the following binding:

    private void bindFields() {
    this.binder = new Binder(Filter.class);
    this.binder.bind(authorField, Filter::getAuthor, null);
    this.binder.forField(countryField).bind(Filter::getCountry, Filter::setCountry);
    this.binder.forField(fromYear).withConverter(new StringToIntegerConverter("Not a number"))
            .bind(Filter::getFromYear, Filter::setFromYear);
    this.binder.forField(toYear).withConverter(new StringToIntegerConverter("Not a number"))
            .bind(Filter::getToYear, Filter::setToYear);
    this.binder.forField(postingDateField).bind(Filter::getPostingDate, Filter::setPostingDate);
    this.binder.forField(tagField).bind(Filter::getTags, Filter::setTags);
    if(filter != null) {
        this.binder.setBean(filter);
        this.binder.readBean(filter);
    }        
}

Comboboxes are populated by following function:

@PostConstruct
private void setDialogItems() {
    ItemFilter<Author> authorFilter = (author, filterString)
            -> author.getLastName().toLowerCase().startsWith(filterString.toLowerCase());
    this.authorField.setItems(authorFilter, api.getAuthors());
    this.countryField.setItems(api.getAllCountries());
    this.tagField.setItems(api.getAllTags());
}

However, when I open the dialog, all combo-boxes are greyed out and inactive. I did test functions that supply data for these combo boxes and they are working fine. Can someone please explain what I am doing wrong and how to correct it.

Besides that, I have one field that does not belong to this bean. It is a century field. I.e. user can define there say number 17 and it will automatically populate fields fromYear and toYear with values 1600 and 1699 respectively.

I can write the code to do the validation and mentioned above population in ValueChangeListener of the century field but wonder if that can be done using the binder.

Gary Greenberg
  • 468
  • 1
  • 9
  • 22

1 Answers1

0

I created a dialog using your code, and authorField displayed as read-only, but countryField and tagField did not—they were fine. The reason authorField displays as read-only is because the setter on its binding is null. Why does that happen? That is explained in the following note in the Javadoc for the Binder::bind method:

Note: when a null setter is given the field will be marked as read-only by invoking (HasValue.setReadOnly(boolean).

Regarding the "Besides that" part of your question, it's better to ask that in a different question so it does not get overlooked and can have its own "accepted answer." Anyway, you can set the binding's getter methods for fromYear and toYear to some logic to compute the appropriate value to display, but I would not do that. With that approach, you would need to re-read OR re-set the bean (you shouldn't do both) in the binder when the century changes to set the values in those fields, but then you'd clobber the other bound field values. The best approach is as you have it—have the century field's ValueChangeListener set the field values for fromYear and toYear.