Binder.setBean(T) not updating fields when using custom PropertySet

Hi all - I’ve created a Form for editing a generic HashMap (which holds configuration properties for our application. We cannot use a Bean because the configuration will vary and the names of the properties aren’t known so we can’t create getters and setters.

I’ve created our own PropertySet and PropertyDefinition classes which seem to work fine. When I first construct the form, it dynamically reads the propertyset and populates the form.

So envision this - on the left side of the page, there is a list of available configurations, and on the right side, all of the fields that the configuration contains. When I first construct the page, the right side is populated with the propertyset of the first configuration on the list.

Following the tutorial (which uses Beans however), I’ve created a method to update the binder whenever a different configuration is selected on the left. The method is definitely being invoked, and we call binder.setBean(T) with the properly selected T, but then nothing happens. The fields on the right still show the values from the first configuration. And none of the log messages from our PropertySet are invoked, e.g. no getGetter or getSetter is called.

Here are some snippets from the code (ACO is short for App Config Object):

In the form constructor:

    ACOPropertySet propertySet = new ACOPropertySet(aco);
    binder = Binder.withPropertySet(propertySet);
	VerticalLayout fieldsLayout = new VerticalLayout();
	... iterate through the propertyset and add textfields for every property {
		TextField c = new TextField(provider.getParameterName());
		fieldsLayout.add(c);
		binder.bind((HasValue<?, ?>) c, provider.getParameterName());
	}

Then, the method that gets invoked when a new config is selected, which is definitely being invoked:

public void setAppConfigObject(AppConfigObject aco) {
    this.aco = aco;
    binder.setBean(aco);
    if (aco != null)
        logger.debug("Setting selected ACO to {}", aco.getName());
    else
        logger.debug("Deselecting any ACO");
}

At some point, again just trying to follow the tutorial, I added this to the constructor but it didn’t have any effect, which I presume is correct since I’m not using a Bean anyway:

    binder.bindInstanceFields(fieldsLayout);

Any advice appreciated!

The Vaadin team can probably give a better answer, but here’s my $0.02.

I’m not sure that you can use the binder because the property names can change. AFAIK withPropertySet has to do with properties for the component itself and not for the fields. bindInstanceFields definitely won’t help as it’s binding on getters and setters that has the same name as the HasValue components (e.g. TextField(“firstname”) will bind to BEAN.getFirstname() and BEAN.setFirstname().

You can build the form quite easily without the binder. You’ll loose the binders ability to buffer changes as well as validation and conversion, so you’ll have to do that elsewhere. However, if it’s just string values, it may work for you.

Here’s a very basic example of using fields without a binder. Because there is no binder, values are written to the HashMap immediately.

import java.util.HashMap;
import com.vaadin.flow.component.Composite;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;

@Route("propertyeditor")
public class PropertyEditor extends Composite<Div> {

   private HashMap<String, String> properties = new HashMap<String, String>();

   public PropertyEditor() {
      properties.put("system", "OSX");
      properties.put("memory", "1000");

      VerticalLayout layout = new VerticalLayout();
      FormLayout form = new FormLayout();

      for (String key : properties.keySet()) {
         TextField tf = new TextField(key, properties.get(key), "");
         tf.addValueChangeListener(e -> properties.put(key, e.getValue()));
         form.addFormItem(tf, key);
      }

      Button save = new Button("Save");
      save.addClickListener(e -> {
         System.out.println("Properties: " + properties);
      });

      layout.add(form);
      layout.add(save);

      getContent().add(layout);
   }

}

Hey Martin- thanks for replying, appreciate it! I discovered that the approach I took with the Binder and the custom propertyset actually does work - there must have been a bug that was fixed in 10.0.4. As soon as I updated from 10.0.2 to 10.0.4 my app worked as expected. Go figure :slight_smile:

Oh and without the bindInstanceFields - as you describe, that has no effect without a bean.

Richard Sand:
Hey Martin- thanks for replying, appreciate it! I discovered that the approach I took with the Binder and the custom propertyset actually does work - there must have been a bug that was fixed in 10.0.4. As soon as I updated from 10.0.2 to 10.0.4 my app worked as expected. Go figure :slight_smile:

Nice!