Search code examples
javavaadinvaadin8

Vaadin 8 - How to bind items of RadioButtonGroup?


I am making a view that has many TextFields and ComboBoxes and CheckBoxes, where their values are handled by a single Binder. No problem there.

BUT: now I want to add 3 RadioButtons to the view, the values should come from the same Binder. Each RadioButton is bound to a different boolean field, only 1 of those fields can be true at the same time (perfect requirement for RadioBoxes).

Problem #1: There is no Component for a simple RadioButton (like there is for CheckBox), i can only find RadioButtonGroup. So i guess I will have to work with that one.

Problem #2: In the Vaadin Docs it specifically says:

The advantages of the CheckBoxGroup component are that as it maintains the individual check box objects, you can get an array of the currently selected items easily, and that you can easily change the appearance of a single component and use it with a Binder.

But I can't find a way to bind the items of RadioButtonGroup, nor can I find any mention of it anywhere.

Is there a way to bind single Items in a RadioButtonGroup?
If not, then I fear I will have to use CheckBoxes where RadioButtons would be a more logical approach.

Here is some code to demonstrate what I am trying to accomplish:

// FooBar Class
private boolean foo = true;
private boolean bar = false;
private boolean fooBar = false;
// constructor, getters and setters

// My View
Binder<FooBar> binder = new Binder<>();
binder.setBean(new FooBar());

// this CheckBox works perfectly fine like this
Checkbox cb = new CheckBox();
cb.setCaption("Foo");
binder.forItem(cb)
    .bind(f -> f.isFoo, (f, b) -> f.setFoo(b));

// this part is where I'm confused
RadioButtonGroup<String> rbg = new RadioButtonGroup<>();
rbg.setItems("Foo", "Bar", "FooBar");
// how can i bind each RadioButton to different fields of my FooBar Bean?
// because getItem() does not exist
binder.forItem(rbg.getItem(0)).bind(f -> f.isFoo,    (f, b) -> f.setFoo(b));
binder.forItem(rbg.getItem(1)).bind(f -> f.isBar,    (f, b) -> f.setBar(b));
binder.forItem(rbg.getItem(2)).bind(f -> f.isFooBar, (f, b) -> f.setFooBar(b));

Solution

  • I suggest to consider a bit different approach. Radio buttons are typically used to assign value to a single attribute - RadioButtonGroup is a single form field - not to a list of attributes or fields so I guess that is why you can not find a straightforward solution.

    If possible change your three booleans to an enum like:

    public enum RadioButtonValue {
       foo, bar, foobar;
    }
    

    This should provide compatible functionality since you wanted to restrict only one of the three booleans to be true at a time.

    Having then class like:

    public class RadioButtonBean {
       @Getter @Setter // Lombok
       private RadioButtonValue radioButtonValue;
       // replaces boolean foo, bar, foobar;
    }
    

    allows you to do the binding easily:

    RadioButtonGroup<RadioButtonValue> radioGroup = new RadioButtonGroup<>();
    radioGroup.setCaption("Radiobutton group");
    // populate with enum values as title or use setItemCaptionGenerator(...);
    radioGroup.setDataProvider(
        new ListDataProvider<RadioButtonValue>( 
            Arrays.asList( RadioButtonValue.values() )
        )
    );
    
    final RadioButtonBean bean = new RadioButtonBean();
    Binder<RadioButtonBean> binder = new Binder<>();
    binder.setBean(bean);
    binder.forField(radioGroup).bind(RadioButtonBean::getRadioButtonValue,
            RadioButtonBean::setRadioButtonValue );
    // uncomment for testing it
    //      radioGroup.addValueChangeListener( vc -> {
    //         Notification.show("bean enum value: "+ bean.getRadioButtonValue() );
    //      });
    

    If it is impossible to change the booleans to enum then I think the easiest way to is to change the above a bit:

    1. Do not bind the radio group at all.
    2. Implement ValueChangeListener that sets corresponding boolean in the bean based on the selected enum.