Search code examples
javaspring-data-jpavaadinvaadin8

ComboBox not editable after data binding


Google-fu revealed nothing, Stackoverflow search revealed similar issues bot not exact to my case. Boy do I ask a lot of questions on here.

This is not an exam question, a homework question, or a project for school or the like. So please don't bother replying with such a comment. That doesn't help me learn

Simply put, I have an item stored in a database:

barcode (bigint), check_out_date (datetime), due_date (datetime), is_available (String: True/False/Null, I thought it'd be easier than a checkbox on the UI side and easier than using bit(1) in the DB as I couldn't get binding right.) is_late (String: True/False/Null, same reason as above), name (varchar), notes (varchar), type (varchar)

is_late and is_available are bound to a combobox of 2 values (True/False), the database reads these values and actually displays the values in the combobox, but I am unable to edit them. For reference, I have 2 other Combobox in different parts of the program, but they are not bound, and thus work.

How can I make (force?) these combobox's to be editable? Thanks for the help, I am glad to be apart of this community!

the issue

Relevant code: If you see formatting/bracket errors it's because I omitted all code that actually works. The program DOES compile and run w/o issues.

private class editPanel extends FormLayout {
    private InventoryItem item;
    private TextField itemName, itemType, itemBarcode;
    private DateField checkOutDate, dueDate;
    private Button save, delete, cancel;
    private ComboBox<String> isAvailable, isLate;
    private TextArea notes;

    private Binder<InventoryItem> binder = new Binder<>(InventoryItem.class); 

    public editPanel() {
        initEditConf();
        initEditLayout();
        addListeners();
        setSizeUndefined();
        Responsive.makeResponsive(this);
        binder.bindInstanceFields(this);
    }

    private void addListeners() {

        isAvailable.addValueChangeListener(e -> {
            System.out.println("Test"); //still not editable with a listener
            //and still not editable by explicitly calling setEnabled(true), setReadOnly(false);
        });


    private void initEditLayout() {

        isAvailable = new ComboBox<String>("Availability");
        isAvailable.setItems("True", "False"); //should be managed by sys too
        isLate = new ComboBox<String>("Overdue");
        isLate.setEnabled(false);
        isLate.setDescription("Value is managed by the system");
        isLate.setIcon(VaadinIcons.QUESTION_CIRCLE_O);
        //isAvailable = new TextField("Availability");
        //isAvailable.setEnabled(false);
        //isLate = new TextField("Overdue");
        //isLate.setEnabled(false);

        cancel.addClickListener(e -> this.cancel());
        save.addClickListener(e -> this.save());
        delete.addClickListener(e -> this.delete());


        binder.forMemberField(checkOutDate).withConverter(new LocalDateToDateConverter());
        binder.forMemberField(dueDate).withConverter(new LocalDateToDateConverter());
        binder.forMemberField(itemName).asRequired().withValidator((string -> string != null && !string.isEmpty()), "Values cannot be empty").bind("name");
        binder.forMemberField(itemType).asRequired().withValidator((string -> string != null && !string.isEmpty()), "Values cannot be empty").bind("type");
        binder.forMemberField(itemBarcode).withConverter(new StringToLongConverter(itemBarcode.getValue())).bind("barcode");
        binder.forMemberField(isAvailable).bind("isAvailable");
        binder.forMemberField(isLate).bind("isLate");
    }

Item class

@Table(name="item")
@Entity 
    public class InventoryItem implements Serializable, Cloneable {

    private static final long serialVersionUID = 5592334329765505365L;
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long barcode;
    @NotNull
    private String name, type;
    @Nullable
    private String notes;
    @Nullable
    private String isAvailable, isLate;
    @Nullable
    @Temporal(TemporalType.TIMESTAMP)
    private Date checkOutDate, dueDate;

    public InventoryItem() {}

    /* Excess constructors omitted */

    @Column(name="barcode")
    public Long getBarcode() {return barcode;}

    public void setBarcode(Long barcode) {this.barcode = barcode;}

    @Column(name="name")
    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    @Column(name="type")
    public String getType() {return type;}

    public void setType(String type) {this.type = type;}

    @Column(name="is_late")
    public String getisLate() {return isLate;}

    public void setLate(String isLate) {this.isLate = isLate;}

    @Column(name="availability")
    public String getisAvailable() {return isAvailable;}

    public void setAvailable(String isAvailable) {this.isAvailable = isAvailable;}

    @Column(name="notes") //bigtext?
    public String getNotes() {return notes;}

    public void setNotes(String notes) {this.notes = notes;}

    @Column(name="check_out_date", columnDefinition="DATETIME")
    public Date getCheckOutDate() {return checkOutDate;}

    public void setCheckOutDate(Date checkOutDate) {this.checkOutDate = checkOutDate;}

    @Column(name="due_date", columnDefinition="DATETIME")
    public Date getDueDate() {return dueDate;}

    public void setDueDate(Date dueDate) {this.dueDate = dueDate;}

}

Solution

  • How embarrassing on my part! turns out my setters were named incorrectly.. hence being able to read but not write.

    The field and instance data is "isAvailable", so the system was looking for InventoryItem.setisAvailable(String available), but the method was named as setAvailable.

    when using binder.forField(field).bind(Object::getter, Object::setter), the solution works. Simply pass your method names. However, binder.forMemberField(field).bind("value") is looking for getValue and setValue methods in your object's class respectively. In this case your options are either modify your getter/setter names or use binder.forField enter image description here