Question

When I enter edit mode of my Table, I want the data validation exclamation mark icon (!) to be shown as soon as the user goes out of bounds of any of the validation constraints.

First, a couple of notes:

  • I'm using Vaadin 7, so the Bean Validation addon sadly won't work.
  • The data validation works as intended.

Now, I have a perfectly working table for which I am using a BeanItemContainer to keep my Person beans inside.

The code for the table and the TableFieldFactory looks something like this:

table.setContainerDataSource(buildContainer());
table.setTableFieldFactory(new TableFieldFactory() {
    @Override
    public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
        TextField field = (TextField) DefaultFieldFactory.get().createField(container, itemId, propertyId,
                uiContext);
        field.setImmediate(true);
        if (propertyId.equals("firstName")) {
            field.addValidator(new BeanValidator(Person.class, "firstName"));
        }
        return field;
    }
});

The Person bean looks as follows:

public class Person {

    @Size(min = 5, max = 50)
    private String firstName;

    ... setters + getters...
}

The problem is that when I type something in the firstName field and then press enter or blur/unfocus that field, no indication whatsoever of error is shown. I have to mouseover the field to see that something is wrong.

My question is two folded...

  1. How do I get the exclamation mark icon to appear when the field is invalid? (This works for a normal TextField that is not in a Table)
  2. Is there a way to get an immediate response from the invalid field (show the icon) (i.e. immediately after you type under 5 chars, without having to press enter or blur/unfocus the field in question).

Would be great if I could have both questions answered! =)

Thanks in advance!

Was it helpful?

Solution

  1. The Caption, Required Indicator (the red asterisk) and - most importantly here - Error Indicator (exclamation mark) are actually provided by the layouts containing the component, not the component themselves. When editable components are displayed in a table, they are displayed without a layout - that's why no error indicator is displayed.

    If I were trying to square this circle, I would look at creating a CustomField as a wrapper for the editable field - and within that CustomField display an error indicator when the wrapped/delegate field becomes invalid. I've not tried this - I've not used editable fields in a table at all - but should be fairly easy to do.

  2. Add a TextChangeListener to the field in FieldFactory, and call field.validate() in the listener. Note, though, that field.getValue() value is not normally changed until blur/unfocus, ergo the validator will be validating the old value - unless you do field.setValue(event.getText()) in the listener. See this post on the Vaadin forum for more details.

This is the sort of thing I meant for a validating wrapper - not tried using it. You'll see initComponent simply returns the field inside a FormLayout, which should give you the icon(s) you're seeking. (You may need to delegate more methods from ValidatingWrapper to delegate than I have- but quick look suggests this may be enough.)

You'd then wrap the field in your tableFieldFactory (second code block)

public class ValidatingWrapper<T> extends CustomField<T> {

  private static final long serialVersionUID = 9208404294767862319L;
  protected Field<T> delegate;

  public ValidatingWrapper(final Field<T> delegate) {
    this.delegate = delegate;

    if (delegate instanceof TextField) {
      final TextField textField = (TextField) delegate;
      textField.setTextChangeEventMode(AbstractTextField.TextChangeEventMode.TIMEOUT);
      textField.setTextChangeTimeout(200);

      textField.addTextChangeListener(new FieldEvents.TextChangeListener() {
        @Override
        public void textChange(FieldEvents.TextChangeEvent event) {
          textField.setValue(event.getText());
          textField.validate();
        }
      });
    }
  }

  @Override
  public Class<? extends T> getType() {
    return delegate.getType();
  }

  @Override
  protected Component initContent() {
    return new FormLayout(delegate);
  }


  @Override
  public Property getPropertyDataSource() {
    return delegate.getPropertyDataSource();
  }

  @Override
  public void setPropertyDataSource(Property newDataSource) {
    delegate.setPropertyDataSource(newDataSource);
  }

}
   

table.setContainerDataSource(buildContainer());
table.setTableFieldFactory(new TableFieldFactory() {
  @Override
  public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
    TextField field = (TextField) DefaultFieldFactory.get().createField(container, itemId, propertyId,
        uiContext);
    field.setImmediate(true);
    if (propertyId.equals("firstName")) {
      field.addValidator(new BeanValidator(Person.class, "firstName"));
    }
    return ValidatingWrapper(field);
  }
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top