How do I bind a check box and a select box to a Java bean? Binding Data to

Hi!

I have my java bean that I want to store values into.

@Data
public class AllComponents implements Serializable{	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	// For start and stop view
	private String[] deviceList;
	private String temperature1;
	private String temperature2;
	private String temperature3;
	private String temperature4;
	private boolean checkbox1;
	private boolean checkbox2;
	private boolean checkbox3;
	private boolean checkbox4;
	private String startStop;
	
	// For database view
	private String[] sampleTimes;
	private String urlServer;
	private String userName;
	private String dataBaseName;
	private String tableName;
	private String connectDisconnet;
	private String password;
	
}

And I set the connection between Vaadin components and my Java bean like this. It’s a Spring Boot application where I begin to load all the components at the start and make them global as well. The problem is that I cannot bind checkboxX, connectDisconnect, deviceList and sampleTimes. I got the following error message.

The method bind(HasValue<?,FIELDVALUE>, String) in the type Binder<AllComponents> is not applicable for the arguments (Checkbox, AllComponents::isCheckbox1)

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder<AllComponents> is not applicable for the arguments (Select<String>, AllComponents::getSampleTimes, AllComponents::setSampleTimes)

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder<AllComponents> is not applicable for the arguments (Button, AllComponents::getConnectDisconnet, AllComponents::setConnectDisconnet)

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder<AllComponents> is not applicable for the arguments (Select<String>, AllComponents::getDeviceList, AllComponents::setDeviceList)

What am I doing wrong here? Sure, I know that it’s not an applicable argument. But what should I do? The poruse with this style of programming is that I’m going to serialize and deserialize the java bean object to a file. Like saving the properties of the Vaadin components.


@Component
@Order(0)
@Data
public class InitialStartUp implements ApplicationListener<ContextRefreshedEvent> {
	
	public static final String SER_DER_PATH = "AllComponents.ser";
	public static final String START = "Start";
	public static final String STOP = "Stop";
	public static final String CONNECT = "Anslut";
	public static final String DISCONNECT = "Koppla ifrån";
	
	// The reason why I have written this project like this
	// it's because of the ability to auto starting and global connection for the Vaadin components
	
	// For the Start_Stop class - Set the default values
	private Select<String> deviceList = new Select<String>(devices());
	private TextField temperature1 = new TextField();
	private TextField temperature2 = new TextField();
	private TextField temperature3 = new TextField();
	private TextField temperature4 = new TextField();
	private Checkbox checkbox1 = new Checkbox();
	private Checkbox checkbox2 = new Checkbox();
	private Checkbox checkbox3 = new Checkbox();
	private Checkbox checkbox4 = new Checkbox();
	private Button startStop = new Button(START);
		
	// For the Database class
	private Select<String> sampleTimes = new Select<String>(new String[] {"1", "10", "30", "60", "120", "600", "1800", "3600"});
	private TextField urlServer = new TextField();
	private TextField userName = new TextField();
	private TextField dataBaseName = new TextField();
	private TextField tableName = new TextField();
	private Button connectDisconnet = new Button(CONNECT);
	private PasswordField password = new PasswordField();
	
	// Field that we want to save to file. It contains all the Vaadin components properties
	private AllComponents allComponents;
	

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
			
		// Check if we have created the storage file
		createFileIfNotExist(SER_DER_PATH);
		
		// Binds the field to AllComponents class
		Binder<AllComponents> binder = new Binder<>(AllComponents.class);
		binder.bind(deviceList, AllComponents::getDeviceList, AllComponents::setDeviceList);
		binder.bind(temperature1, AllComponents::getTemperature1, AllComponents::setTemperature1);
		binder.bind(temperature2, AllComponents::getTemperature2, AllComponents::setTemperature2);
		binder.bind(temperature3, AllComponents::getTemperature3, AllComponents::setTemperature3);
		binder.bind(temperature4, AllComponents::getTemperature4, AllComponents::setTemperature4);
		binder.bind(checkbox1, AllComponents::isCheckbox1);
		binder.bind(checkbox2, AllComponents::isCheckbox2);
		binder.bind(checkbox3, AllComponents::isCheckbox3);
		binder.bind(checkbox4, AllComponents::isCheckbox4);
		binder.bind(startStop, AllComponents::getStartStop);
		binder.bind(sampleTimes, AllComponents::getSampleTimes, AllComponents::setSampleTimes);
		binder.bind(urlServer, AllComponents::getUrlServer, AllComponents::setUrlServer);
		binder.bind(userName, AllComponents::getUserName, AllComponents::setUserName);
		binder.bind(dataBaseName, AllComponents::getDataBaseName, AllComponents::setDataBaseName);
		binder.bind(tableName, AllComponents::getTableName, AllComponents::setTableName);
		binder.bind(connectDisconnet, AllComponents::getConnectDisconnet, AllComponents::setConnectDisconnet);
		binder.bind(password, AllComponents::getPassword, AllComponents::setPassword);
		
		// Set connection
		allComponents = new SerDer().deserialization(SER_DER_PATH);
		binder.setBean(allComponents);
	}

The method bind(HasValue<?,FIELDVALUE>, String) in the type Binder is not applicable for the arguments (Checkbox, AllComponents::isCheckbox1)

The syntax binder.bind(checkbox, booleanValueProvider) doesn’t exist. replace it with binder.bind(checkbox, booleanValueProvider, booleanSetter). Using your variables, this becomes: binder.bind(checkbox1, AllComponents::isCheckbox1, AllComponents::setCheckbox1);

Or if you want the field to be read-only, then: binder.bind(checkbox, booleanValueProvider, booleanSetter). Using your variables, this becomes: binder.bind(checkbox1, AllComponents::isCheckbox1, null);

small note here: I recommend always using a slightly different syntax: binder.forField(hasValueField).bind(getter, setter); because there you can provide Validators, Converters before chaining the .bind(..) call. See [Validating and Converting User Input]
(https://vaadin.com/docs/v14/flow/binding-data/tutorial-flow-components-binder-validation.html)

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder is not applicable for the arguments (Select, AllComponents::getSampleTimes, AllComponents::setSampleTimes)

sampleTimes is of Type String[], not String. Therefore the binding doesn’t work like that. In fact, there is no good solution to binding a List of items with the binder. Ususally, Lists of things are displayed using a grid. But the grid does not implement HasValue so you can’t bind a grid. You’ll have to fill the grid with grid.setItems(allComponents.getSampleTimes());. If you want to allow adding and removing of items to that list, you can add buttons that will modify the actual list of sampleTimes in your bean, then refresh the grid again with the current sampleTimes of the allComponents bean. Editing can be done with the [grid editor]
(https://vaadin.com/components/vaadin-grid/java-examples/grid-editor), or by defining the single column as a textfield with a valueChangeListener.

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder is not applicable for the arguments (Button, AllComponents::getConnectDisconnet, AllComponents::setConnectDisconnet)

Button does not implement HasValue and is therefore not eligible for using it with a binder. Did you mean to use a TextField?

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder is not applicable for the arguments (Select, AllComponents::getDeviceList, AllComponents::setDeviceList)

Same as with sampleTimes, the deviceList is of Type String[], not String.

Kaspar Scherrer:

The method bind(HasValue<?,FIELDVALUE>, String) in the type Binder is not applicable for the arguments (Checkbox, AllComponents::isCheckbox1)

The syntax binder.bind(checkbox, booleanValueProvider) doesn’t exist. replace it with binder.bind(checkbox, booleanValueProvider, booleanSetter). Using your variables, this becomes: binder.bind(checkbox1, AllComponents::isCheckbox1, AllComponents::setCheckbox1);

Or if you want the field to be read-only, then: binder.bind(checkbox, booleanValueProvider, booleanSetter). Using your variables, this becomes: binder.bind(checkbox1, AllComponents::isCheckbox1, null);

small note here: I recommend always using a slightly different syntax: binder.forField(hasValueField).bind(getter, setter); because there you can provide Validators, Converters before chaining the .bind(..) call. See [Validating and Converting User Input]
(https://vaadin.com/docs/v14/flow/binding-data/tutorial-flow-components-binder-validation.html)

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder is not applicable for the arguments (Select, AllComponents::getSampleTimes, AllComponents::setSampleTimes)

sampleTimes is of Type String[], not String. Therefore the binding doesn’t work like that. In fact, there is no good solution to binding a List of items with the binder. Ususally, Lists of things are displayed using a grid. But the grid does not implement HasValue so you can’t bind a grid. You’ll have to fill the grid with grid.setItems(allComponents.getSampleTimes());. If you want to allow adding and removing of items to that list, you can add buttons that will modify the actual list of sampleTimes in your bean, then refresh the grid again with the current sampleTimes of the allComponents bean. Editing can be done with the [grid editor]
(https://vaadin.com/components/vaadin-grid/java-examples/grid-editor), or by defining the single column as a textfield with a valueChangeListener.

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder is not applicable for the arguments (Button, AllComponents::getConnectDisconnet, AllComponents::setConnectDisconnet)

Button does not implement HasValue and is therefore not eligible for using it with a binder. Did you mean to use a TextField?

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder is not applicable for the arguments (Select, AllComponents::getDeviceList, AllComponents::setDeviceList)

Same as with sampleTimes, the deviceList is of Type String[], not String.

Thanks! This solved my problem.

The method bind(HasValue<?,FIELDVALUE>, ValueProvider<AllComponents,FIELDVALUE>, Setter<AllComponents,FIELDVALUE>) in the type Binder is not applicable for the arguments (Button, AllComponents::getConnectDisconnet, AllComponents::setConnectDisconnet)

Button does not implement HasValue and is therefore not eligible for using it with a binder. Did you mean to use a TextField?

No. It’s a button. I only want to change the lable of the button.

Access the setText(String text) method.

No. It’s a button. I only want to change the lable of the button.
Access the setText(String text) method.

I’m not sure if this is a question or your own solution to the problem, forgive me if I answer anyway. To only change the text appearing on the button, startStop.setText(allComponents.getStartStop()); will do. No need to involve the binder. (you’d have to instance allComponents earlier, or move that line further down after allComponents is instanced)

Kaspar Scherrer:

No. It’s a button. I only want to change the lable of the button.
Access the setText(String text) method.

I’m not sure if this is a question or your own solution to the problem, forgive me if I answer anyway. To only change the text appearing on the button, startStop.setText(allComponents.getStartStop()); will do. No need to involve the binder. (you’d have to instance allComponents earlier, or move that line further down after allComponents is instanced)

But if the button lable change, then the bean need to change as well?
You mean that that can be done from: allComponents.setStartStop(startStop.getText()) ?

The reason for this is that I have a push button. When I press it, it will change lable text from “ON” to "OFF, and vise versa.

I know that I could use a listener for this, which I do. But for the connection between the bean and the vaadin component is the question.

Thanks!

But if the button lable change, then the bean need to change as well?

Okay so you really do want to bind the allComponents.startStop value. But binding a value on a Button is simply not possible. The combination of Binder and Button is not compatible. The button would need to implement HasValue. If you really want to, it is possible to create your own Component that is basically a Button but actually implements HasValue and when calling getValue you could read the current text of the button. I can’t guide you there, but I’m pretty sure it’s possible.

Other options:

  1. add a ClickListener to the button that sets its own text to “ON”/“OFF”, and also changes the value in the allComponents bean. Use this option if it really really must be a Button, and you don’t feel confident enough to do the above mentioned HasValue-Button. No Binder involved here.

  2. Don’t use a Button at all, instead you could use a textfield. (but it will feel weird if you only allow the texts “ON” or “OFF”)

  3. Use a CheckBox, and add a custom Converter to the binding (Because the two states “ON” and “OFF” can basically be represented by true and false). Personally, I would go for this option.

I was actually intrigued by my last option so I tried to implement it myself as an exercise. I tested it, it works fantastically. Here is the code:

@Route(value = "Test", layout = MainView.class)
public class TestView extends VerticalLayout {

    public TestView() {
        Foo foo = new Foo();
        Binder<Foo> binder = new Binder<>();
		binder.setBean(foo);

        add(new Button("Show current value of startStop", click -> {
            Notification.show(foo.getStartStop());
        }));

        Checkbox startStopCheckbox = new Checkbox("is running");
        add(startStopCheckbox);

        binder.forField(startStopCheckbox)
                .withConverter(new BooleanToOnOffStringConverter())
                .bind(Foo::getStartStop, Foo::setStartStop);
    }

    public class Foo {
        private String startStop;
        public Foo(){
            this.startStop = "ON";
        }
        public String getStartStop() {
            return startStop;
        }
        public void setStartStop(String startStop) {
            this.startStop = startStop;
        }
    }

    public class BooleanToOnOffStringConverter implements com.vaadin.flow.data.converter.Converter<Boolean, String> {

        @Override
        public Result<String> convertToModel(Boolean value, ValueContext context) {
            return value ? Result.ok("ON") : Result.ok("OFF");
        }

        @Override
        public Boolean convertToPresentation(String value, ValueContext context) {
            return value.equals("ON");
        }
    }
}

Kaspar Scherrer:

But if the button lable change, then the bean need to change as well?

Okay so you really do want to bind the allComponents.startStop value. But binding a value on a Button is simply not possible. The combination of Binder and Button is not compatible. The button would need to implement HasValue. If you really want to, it is possible to create your own Component that is basically a Button but actually implements HasValue and when calling getValue you could read the current text of the button. I can’t guide you there, but I’m pretty sure it’s possible.

Other options:

  1. add a ClickListener to the button that sets its own text to “ON”/“OFF”, and also changes the value in the allComponents bean. Use this option if it really really must be a Button, and you don’t feel confident enough to do the above mentioned HasValue-Button. No Binder involved here.

  2. Don’t use a Button at all, instead you could use a textfield. (but it will feel weird if you only allow the texts “ON” or “OFF”)

  3. Use a CheckBox, and add a custom Converter to the binding (Because the two states “ON” and “OFF” can basically be represented by true and false). Personally, I would go for this option.

I was actually intrigued by my last option so I tried to implement it myself as an exercise. I tested it, it works fantastically. Here is the code:

@Route(value = "Test", layout = MainView.class)
public class TestView extends VerticalLayout {

    public TestView() {
        Foo foo = new Foo();
        Binder<Foo> binder = new Binder<>();
		binder.setBean(foo);

        add(new Button("Show current value of startStop", click -> {
            Notification.show(foo.getStartStop());
        }));

        Checkbox startStopCheckbox = new Checkbox("is running");
        add(startStopCheckbox);

        binder.forField(startStopCheckbox)
                .withConverter(new BooleanToOnOffStringConverter())
                .bind(Foo::getStartStop, Foo::setStartStop);
    }

    public class Foo {
        private String startStop;
        public Foo(){
            this.startStop = "ON";
        }
        public String getStartStop() {
            return startStop;
        }
        public void setStartStop(String startStop) {
            this.startStop = startStop;
        }
    }

    public class BooleanToOnOffStringConverter implements Converter<Boolean, String> {

        @Override
        public Result<String> convertToModel(Boolean value, ValueContext context) {
            return value ? Result.ok("ON") : Result.ok("OFF");
        }

        @Override
        public Boolean convertToPresentation(String value, ValueContext context) {
            return value.equals("ON");
        }
    }
}

Thanks! The option 1 works fine.

I have a question related tho this question.

When I load the fields

// Set connection
allComponents = new SerDer().deserialization(SER_DER_PATH);
binder.setBean(allComponents);

They fields are becoming null.

public class SerDer{


	public void serialization(String filePath, AllComponents allComponents) {
		try {
			FileOutputStream fileOut = new FileOutputStream(filePath);
			ObjectOutputStream out = new ObjectOutputStream(fileOut);
			out.writeObject(allComponents);
			out.close();
			fileOut.close();
		} catch (IOException e) {
			System.out.println("Feil på filen");
		}
	}

	/**
	 * Load the Vaadin components
	 */
	public AllComponents deserialization(String filePath) {
		try {
			FileInputStream fileIn = new FileInputStream(filePath);
			ObjectInputStream in = new ObjectInputStream(fileIn);
			AllComponents allComponents = (AllComponents) in.readObject();
			in.close();
			fileIn.close();
			return allComponents;
		} catch (IOException | ClassNotFoundException  e) {
			System.out.println("Fel på filen");
		}
		return null;

	}
}

Se when I save them, they are still null.

new SerDer().serialization(InitialStartUp.SER_DER_PATH, allComponents);

Here is the complete code. The problem with this code is that the bean will not be binded to the components. When I restart my Vaadin application, then all fields are empty. They should contain text.

@Component
@Order(0)
@Data
public class InitialStartUp implements ApplicationListener<ContextRefreshedEvent> {
	
	public static final String SER_DER_PATH = "AllComponents.ser";
	public static final String START = "Start";
	public static final String STOP = "Stop";
	public static final String CONNECT = "Anslut";
	public static final String DISCONNECT = "Koppla ifrån";
	
	// The reason why I have written this project like this
	// it's because of the ability to auto starting and global connection for the Vaadin components
	
	// For the Start_Stop class - Set the default values
	private Select<String> deviceList = new Select<String>(devices());
	private TextField temperature1 = new TextField();
	private TextField temperature2 = new TextField();
	private TextField temperature3 = new TextField();
	private TextField temperature4 = new TextField();
	private Checkbox checkbox1 = new Checkbox("Temperature 1");
	private Checkbox checkbox2 = new Checkbox("Temperature 2");
	private Checkbox checkbox3 = new Checkbox("Temperature 3");
	private Checkbox checkbox4 = new Checkbox("Temperature 4");
	private Button startStop = new Button(START);
		
	// For the Database class
	private Select<String> sampleTimes = new Select<String>("1", "10", "30", "60", "120", "600", "1800", "3600");
	private TextField urlServer = new TextField();
	private TextField userName = new TextField();
	private TextField dataBaseName = new TextField();
	private TextField tableName = new TextField();
	private Button connectDisconnet = new Button(CONNECT);
	private PasswordField password = new PasswordField();
	
	// Field that we want to save to file. It contains all the Vaadin components properties
	private AllComponents allComponents;
	private Binder<AllComponents> binder;
	

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
			
		// Check if we have created the storage file
		createFileIfNotExist(SER_DER_PATH);
		
		// Binds the field to AllComponents class
		binder = new Binder<>(AllComponents.class);
		binder.bind(deviceList, AllComponents::getDevice, AllComponents::setDevice);
		binder.bind(temperature1, AllComponents::getTemperature1, AllComponents::setTemperature1);
		binder.bind(temperature2, AllComponents::getTemperature2, AllComponents::setTemperature2);
		binder.bind(temperature3, AllComponents::getTemperature3, AllComponents::setTemperature3);
		binder.bind(temperature4, AllComponents::getTemperature4, AllComponents::setTemperature4);
		binder.bind(checkbox1, AllComponents::isCheckbox1, AllComponents::setCheckbox1);
		binder.bind(checkbox2, AllComponents::isCheckbox2, AllComponents::setCheckbox2);
		binder.bind(checkbox3, AllComponents::isCheckbox3, AllComponents::setCheckbox3);
		binder.bind(checkbox4, AllComponents::isCheckbox4, AllComponents::setCheckbox4);
		//binder.bind(startStop, AllComponents::getStartStop); // I will replace this with a listener
		binder.bind(sampleTimes, AllComponents::getSampleTime, AllComponents::setSampleTime);
		binder.bind(urlServer, AllComponents::getUrlServer, AllComponents::setUrlServer);
		binder.bind(userName, AllComponents::getUserName, AllComponents::setUserName);
		binder.bind(dataBaseName, AllComponents::getDataBaseName, AllComponents::setDataBaseName);
		binder.bind(tableName, AllComponents::getTableName, AllComponents::setTableName);
		//binder.bind(connectDisconnet, AllComponents::getConnectDisconnet, AllComponents::setConnectDisconnet); // I will replace this with a listener
		binder.bind(password, AllComponents::getPassword, AllComponents::setPassword);
		
		// Set connection
		loadVaadinComponents();
	}
	
	/**
	 * Save the bean to a file
	 */
	public void saveVaadinComponents() {
		new SerDer().serialization(InitialStartUp.SER_DER_PATH, allComponents);
	}
	
	/**
	 * Load the bean then binds bean to the vaadin components
	 */
	public void loadVaadinComponents() {
		allComponents = new SerDer().deserialization(SER_DER_PATH);
		binder.setBean(allComponents);
		
	}

	/**
	 * This function is used for create the .ser file if they don't exist at the beginning
	 * @param filePath
	 */
	private void createFileIfNotExist(String filePath) {
		File file = new File(filePath);
		if(file.exists() == false) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println("Filen " + filePath + " finns");
		}
		
	}

	/**
	 * This function is used to get all the COM devices as strings
	 * @return
	 */
	private String[] devices() {
		SerialPort[] coms = SerialPort.getCommPorts();
		String[] list = new String[coms.length]
;
		for(int i = 0; i < coms.length; i++)
			list[i]
 = coms[i]
.getSystemPortName();
		return list;
	}
}

You should debug your application to see where the null values are coming from. It sounds to me as if your serialization logic doesn’t work as intended. I am unexperienced in this way of serialization so I can’t help you by looking at the code.