CustomField with Binder

Hi,

I’ve created custom datetime picker using CustomField, witch works with timestamps. The problem is that when I set binder asRequired() it tells me that field is empty (blue dot) even if both inner fields are filled. I have checked and getValue() gives me not empty value. What should I change or override to get this working?

public class DateTimePicker extends CustomField<Long> {
	private final DatePicker datePicker = new DatePicker();
	private final TimePicker timePicker = new TimePicker();

	public DateTimePicker(String label) {
		setLabel(label);
		add(datePicker, timePicker);
	}

	@Override
	public Long generateModelValue() {
		final LocalDate date = datePicker.getValue();
		final LocalTime time = timePicker.getValue();

		if(date != null && time != null) {
			return Timestamp.valueOf(LocalDateTime.of(date, time)).getTime();
		} 

		return null;
	}

	@Override
	public void setPresentationValue(Long newPresentationValue) {
		LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(newPresentationValue), ZoneId.systemDefault());

		datePicker.setValue(newPresentationValue != null ? ldt.toLocalDate() : null);
		timePicker.setValue(newPresentationValue != null ? ldt.toLocalTime() : null);
	}
}

Not sure, I haven’t tested this. Can you change the constructor as follows?

public DateTimePicker(String label) {
	super(0L);
	setLabel(label);
	add(datePicker, timePicker);
	setPresentationValue(getEmptyValue());
}

Thank you for the answer. It fixed half of the problem. I am now able to writeBean() properly but it is still marked with blue dot like if it was empty.

On a CustomField I have (an Icon checkfield) I had to @Override the getValue and setValue since the binder would not work properly with the ‘default’ ones… Now it seems to work properly.
Try doing that and see how it works…

My Code:


public class CheckBoxCustomField extends CustomField<Boolean>{

	private static final long serialVersionUID = 1L;
	
	private Icon icon = UIUtils.createDisabledIcon(VaadinIcon.CIRCLE);
	private boolean value = false;
	
	public CheckBoxCustomField() {
		icon.addClickListener(event ->{
			value = !value;
			toggleValueChange();
		});
		
		add(icon);
	}
	
	@Override
	protected Boolean generateModelValue() {
		return getValue();
	}

	@Override
	protected void setPresentationValue(Boolean newPresentationValue) {
		setValue(newPresentationValue);
	}

	private void toggleValueChange() {
		if(value) {
			icon.getElement().setAttribute("icon", "vaadin" + ':' + "check-circle");
			icon.getElement().getStyle().set("color", "var(--lumo-success-text-color)");
		}else {
			icon.getElement().setAttribute("icon", "vaadin" + ':' + "circle");
			icon.getElement().getStyle().set("color", "var(--lumo-disabled-text-color)");
		}
	}
	
	//Gets and Sets are needed for the Binder...
	
	@Override
	public void setValue(Boolean value) {
		this.value = value;
		toggleValueChange();
	}
	
	@Override
	public Boolean getValue() {
		return value;
	}
}

Binder still telling me that field is empty.

Sebastian Ciesielski:
Thank you for the answer. It fixed half of the problem. I am now able to writeBean() properly but it is still marked with blue dot like if it was empty.

CustomField doesn’t track the value changes of the internal fields. You will require the following I guess:

public DateTimePicker(String label) {
	super(0L);
	setLabel(label);
	add(datePicker, timePicker);
	setPresentationValue(getEmptyValue());
	datePicker.addValueChangeListener(e -> {
		if(e.isFromClient()) {
			updateValue();
        }
     });
	timePicker.addValueChangeListener(e -> {
		if(e.isFromClient()) {
			updateValue();
        }
     });
}

It is code that combines both answers but unfortunately still not working.

public class DateTimePicker extends CustomField<Long> {
	private final DatePicker datePicker = new DatePicker();
	private final TimePicker timePicker = new TimePicker();

	public DateTimePicker(String label) {
		super(0L);
		setLabel(label);

		datePicker.addValueChangeListener(e -> {
			if(e.isFromClient()) {
				updateValue();
			}
		});
		timePicker.addValueChangeListener(e -> {
			if(e.isFromClient()) {
				updateValue();
			}
		 });

		HorizontalLayout hl = new HorizontalLayout(datePicker, timePicker);
		hl.setSpacing(true);
		add(hl);
		setPresentationValue(getEmptyValue());
	}

	@Override
	public Long generateModelValue() {
		final LocalDate date = datePicker.getValue();
		final LocalTime time = timePicker.getValue();

		if(date != null && time != null) {
			return Timestamp.valueOf(LocalDateTime.of(date, time)).getTime();
		} 

		return null;
	}

	@Override
	public void setPresentationValue(Long newPresentationValue) {
		LocalDateTime ldt = LocalDateTime.ofInstant(Instant.ofEpochMilli(newPresentationValue), ZoneId.systemDefault());

		datePicker.setValue(newPresentationValue != null ? ldt.toLocalDate() : null);
		timePicker.setValue(newPresentationValue != null ? ldt.toLocalTime() : null);
	}

	@Override
	public void setValue(Long value) {
		setPresentationValue(value);
	}

	@Override
	public Long getValue() {
		return generateModelValue();
	}
}

There is no need to override setValue method. If you want to override, you need to call the super.

	@Override
	public void setValue(Long value) {
		super.setValue(value);
		setPresentationValue(value);
	}

Unfortunately both the versions (override setValue with super call and without override setValue) do not solve the problem.