Vaadin 8 Binder

Hi,

I am trying to create a form object extending Formlayout with binder inside. So I already bound my textfields this way

// Bind fields to entity properties by naming convention binder = new Binder<>(Item.class); binder.forField(price) .withConverter(new StringToBigDecimalConverter(new BigDecimal(10.00), "Price must be in ##.## format")) .bind(Item::getPrice, Item::setPrice); binder.forField(name).bind(Item::getName, Item::setName); binder.forField(description).bind(Item::getDescription, Item::setDescription); name, description and price are all textfields they are all string types except the price which is BigDecimal. I am having all this code in the form class constructor. When I am trying to initialize my binder by calling

binder.setBean(new Item()

Here is the exception:

SEVERE: Servlet.service() for servlet [dispatcherServlet]
 in context with path [] threw exception [com.vaadin.server.ServiceException: java.lang.NullPointerException: value cannot be null]
 with root cause
java.lang.NullPointerException: value cannot be null

it throws
NullPointerException
. I used to bind this way in Vaadin 7 without any issues. I have another form where I don’t explecitly bind my fields I do bind the by calling this on the binder object

binder.bindInstanceFields(CustomerForm.this);

This works fine for the other view where I get the exception which is the item view I need to bind manually since I have a BigDecimal field.

Thanks

This short sample works for me. What am I doing differently than you?

public class MyUI extends UI {

public class Item {
  private String name;
  private BigDecimal price;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public BigDecimal getPrice() {
    return price;
  }
  public void setPrice(BigDecimal price) {
    this.price = price;
  }
}

@Override
protected void init(VaadinRequest vaadinRequest) {
  final VerticalLayout layout = new VerticalLayout();
  TextField price = new TextField();
  TextField name = new TextField();
  Binder<Item> binder = new Binder<>(Item.class);
  binder.forField(price)
    .withConverter(new StringToBigDecimalConverter(new BigDecimal(10.00), "Price must be in ##.## format"))
    .bind(Item::getPrice, Item::setPrice);
  binder.forField(name).bind(Item::getName, Item::setName);

  layout.addComponents(price, name);
    setContent(layout);
  }


@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
  public static class MyUIServlet extends VaadinServlet {
  }
}

Thanks for the reply. I am using all this in a formlayout. I get a NullPointerException when I call this:

binder.setBean(new Item());

Put a breakpoint on the row and see if it’s called before binder is constructed, if there is a NPE in the Item-constructor or something like that…

The problem is that the bean’s values are null, and TextField in Vaadin 8 can’t handle null and will fail with NPE as above mentioned. Since I believe a lot of programmers will bump into this, I have created a bug for this:
https://github.com/vaadin/framework/issues/8664
The bug report also contains a workaround.

Marked as a bug. Thanks for the reporting and for the ticket

I had the same NPE issue with
StringToIntegerConverter
. Next line solved my problem.

binder.withConverter(Integer::valueOf, value -> (value == null) ? "" : String.valueOf(value), "Must be a number"))

In your case this should pass without NPE:

binder.forField(price)
   .withConverter(value -> value.isEmpty() ? new BigDecimal("10.00") : new BigDecimal(value),
                  value -> value == null ? "" : value.toString(),
                  "Price must be in ##.## format")
   .bind(Item::getPrice, Item::setPrice);

I had the same NPE issue with StringToBigDecimalConverter.
The solution of C.Berg doesn’t work for me because it doesn’t handle numbers in non english format.
The workaround of Martin Vyšný (https://github.com/vaadin/framework/issues/8664) is the best solution because it uses a NumberFormat with the current locale.

In your case it would look as follows:

binder.forField(price)
    .withNullRepresentation("")
    .withConverter(new StringToBigDecimalConverter("Price must be in ##.## format"))
    .bind(Item::getPrice, Item::setPrice);

This results in an empty field, if the price is null and returns null, if the field is empty.