NumberField for Integer and How to bind a nested property in Vaadin 13

Hello, I have a class (MedicalJournalEntry) which has a complex property of type BloodPressure (see below)

How do I bind this?

I found something in the forum about this and wrote this code, but it doesn’t work:

	NumberField intSYS = new NumberField("systolischer Blutdruck");
	NumberField intDIA = new NumberField("diastolischer Blutdruck");

	// the new entity
	MedicalJournalEntry entry;
	// the binder
	BeanValidationBinder<MedicalJournalEntry> binder = new BeanValidationBinder<>(MedicalJournalEntry.class);
	
	// bind normal primitive properties and set bean
    binder.setBean(entry);
    binder.bindInstanceFields(this);
	
	// bind nested object, this causes the problems
    binder.forField(intSYS).bind(
    		entry -> {
    			if (entry.getBloodPressure() != null) {
    				return entry.getBloodPressure().getIntSYS();
    			}
    			return 0d;
    		},
    		  (entry, intSYS) -> {
    			    if (entry.getBloodPressure() != null) {
    			    	entry.getBloodPressure().setIntSYS(intSYS);
    				} else {
    				  // i.e. create a new address object
    				  BloodPressure bP = new BloodPressure();
    				  bP.setIntSYS(intSYS);
    				  entry.setBloodPressure(bP);
    				}
    		});

entry is of type MedicalJournalEntry

My class MedicalJournalEntry:

public class MedicalJournalEntry extends JournalEntry{
// ...
@Embedded
private BloodPressure bloodPressure;
//..
}

My class BloodPressure:

public class BloodPressure {

public static final int MAX_SYS_BLOODPRESSURE = 250;
public static final int MIN_SYS_BLOODPRESSURE = 50;

public static final int MAX_DIA_BLOODPRESSURE = 230;
public static final int MIN_DIA_BLOODPRESSURE = 30;

@Column(name ="BLOOD_PRESSURE_SYS")
@Min(value= MIN_SYS_BLOODPRESSURE, message ="Der systolische Blutdruck muss mindestens " + MIN_SYS_BLOODPRESSURE + " mmHg betragen")
@Max(value=MAX_SYS_BLOODPRESSURE, message = "Der systolische Blutdruck darf maximal " + MAX_SYS_BLOODPRESSURE + " mmHg betragen")
private int intSYS;

@Column(name = "BLOOD_PRESSURE_DIA")
@Min(value= MIN_DIA_BLOODPRESSURE, message ="Der diastolische Blutdruck muss mindestens " + MIN_DIA_BLOODPRESSURE + " mmHg betragen")
@Max(value=MAX_DIA_BLOODPRESSURE, message = "Der diastolische Blutdruck darf maximal " + MAX_DIA_BLOODPRESSURE + " mmHg betragen")
private int intDIA;

public BloodPressure() {}

public BloodPressure(int intSYS, int intDIA) {
	this.intSYS = intSYS;
	this.intDIA = intDIA;
}

public int getIntSYS() {
	return intSYS;
}

public void setIntSYS(int intSYS) {
	this.intSYS = intSYS;
}

public int getIntDIA() {
	return intDIA;
}

public void setIntDIA(int intDIA) {
	this.intDIA = intDIA;
}

}

Following error messages appear:

Lambda expression's parameter entry cannot redeclare another local variable defined in an enclosing scope. 

The method setIntSYS(int) in the type BloodPressure is not applicable for the arguments (Double)

The errors seem pretty clear to me (even though I’m not too familiar with this Java API myself, I’m mostly doing JS and other frontend stuff).

Lambda expression's parameter entry cannot redeclare another local variable defined in an enclosing scope.

Looks like you already have a NumberField intSYS in the top scope so you can’t use the same variable name intSYS for a parameter name of the lambda (entry, intSYS) -> {...

The method setIntSYS(int) in the type BloodPressure is not applicable for the arguments (Double)

I guess here the problem is that setIntSYS(int) only takes an int but it’s getting a Double I guess from return 0d;

You can use a Converter to change the types of bound fields to match your data types. Something like this:

binder.forField(intSYS).withConverter(new Converter<Double, Integer>() {
            @Override
            public Result<Integer> convertToModel(Double aDouble, ValueContext valueContext) {
                return Result.ok(aDouble.intValue());
            }

            @Override
            public Double convertToPresentation(Integer integer, ValueContext valueContext) {
                return new Double(integer);
            }
        }).bind(
                entry -> {
                    if (entry.getBloodPressure() != null) {
                        return entry.getBloodPressure().getIntSYS();
                    }
                    return 0;
                },
                (entry, intS) -> {
                    if (entry.getBloodPressure() != null) {
                        entry.getBloodPressure().setIntSYS(intS);
                    } else {
                        // i.e. create a new address object
                        BloodPressure bP = new BloodPressure();
                        bP.setIntSYS(intS);
                        entry.setBloodPressure(bP);
                    }
                });

Thank you very much, this helped me a lot! :smiley:

But I got one last question: The BeanValidationBinder doesn’t seem to understand my Javax validation constraints in the class definitions, like @max and @min. It works for the primitive attributes, but not for a nested one like BloodPressure.

Do I need to do this manually somehow?

Only the fields that are bound with bindInstanceFields are eligible for Bean Validation. The nested property has a custom binding binder.forField().bind(), so bean validation constraints doesn’t apply there. The easiest way around this would be to define custom validators for this property with binder.forfield(...).withValidator(...).bind(...). You could use an IntegerRangeValidator to define minimum and maximum values.

-Olli

It looks like the latest version of Vaadin Flow (14.1.21) includes an IntegerField class in the com.vaadin.flow.component.textfield package. I don’t see it documented on the site yet.

There’s an IntegerField example here https://vaadin.com/components/vaadin-text-field/java-examples/number-field

The components section at vaadin.com can be slightly confusing in some cases as not all component classes are explicitly represented in the “component list”. IntegerField is part of the vaadin-text-field package.

If you’re looking for a specific Java class (for a component) it’s easiest to search on https://vaadin.com/api/