12

I am using a Vaadin text field and I want to restrict it to support numbers only in it. I tried to override setValue() and return without calling super. setValue() if text is not a number. But it doesn't seems to be working. How can I correct this? I am using Vaadin 7. And I think it doesn't support NumberField as well.

Abimaran Kugathasan
  • 26,826
  • 11
  • 67
  • 101
Sanjaya Liyanage
  • 4,366
  • 9
  • 33
  • 50

7 Answers7

20

If I understand you question correct, you want to have a field that ignores all inputs that are not a number and not only mark the field as invalid. Vaadins architecture is designed that every field in the browser has its representation on the server. In my opinion the cleanest way to achieve this would be to have a browser field, that permits input of letters and other wrong characters. I couldn't find such a field in Vaadin 7. There seems to be an add-on for vaadin 6 called Number Field for that, but I didn't test it.
You have multiple options:

  1. Port this add-on to vaadin 7 or ask the author to do it

  2. Write your own field. Maybe extending VTextField and TextFieldConnector

  3. Do everything on the server side and accept the delays and the traffic (IMHO ugly)

Since I think option 3 is not the way to go, I probably shouldn't show this code, but it's the quickest way to implement this.

public class IntegerField extends TextField implements TextChangeListener {
String lastValue;

public IntegerField() {
    setImmediate(true);
    setTextChangeEventMode(TextChangeEventMode.EAGER);
    addTextChangeListener(this);
}

@Override
public void textChange(TextChangeEvent event) {
    String text = event.getText();
    try {
        new Integer(text);
        lastValue = text;
    } catch (NumberFormatException e) {
        setValue(lastValue);
    }
}
}
Ahmed Ashour
  • 4,209
  • 10
  • 29
  • 46
raffael
  • 2,297
  • 4
  • 25
  • 42
  • thanks dude. It works fine. I added some null check and empty check and handled them. – Sanjaya Liyanage Jun 24 '13 at 10:30
  • Working but when typing lots of characters quickly you can still enter unwanted content – Eyal Sep 11 '14 at 16:25
  • 1
    Its generally considered bad practice to use exceptions for control flow. See http://c2.com/cgi/wiki?DontUseExceptionsForFlowControl, http://programmers.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why or http://stackoverflow.com/questions/729379/why-not-use-exceptions-as-regular-flow-of-control. – s.froehlich Dec 02 '14 at 13:59
  • 1
    Thanks for the code Raffael, but it is important to said, that this is not the proper solution, cause to delays in comunication the value can accept wrongs values or deny valid values. – Diego Quirós Jul 15 '15 at 02:24
12

Vaadin 7 allows to extend their built in widgets (if you want to have more knowledge on this I really recommend this post) here is a solution which uses that mechanism.

It is composed of two classes: Connector and the Extension

  1. The Extension

    package com.infosystem.widgets.vaadin;
    import com.vaadin.server.AbstractClientConnector;
    import com.vaadin.server.AbstractExtension;
    import com.vaadin.ui.TextField;
    
    public class NumberField extends AbstractExtension {
    
            public static void extend(TextField field) {
                new NumberField().extend((AbstractClientConnector) field);
            }
    }
    
  2. Connector:

    package com.infosystem.widgets.vaadin.client.numberField;
    import com.google.gwt.event.dom.client.KeyCodes;
    import com.google.gwt.event.dom.client.KeyPressEvent;
    import com.google.gwt.event.dom.client.KeyPressHandler;
    import com.infosystem.widgets.vaadin.NumberField;
    import com.vaadin.client.ComponentConnector;
    import com.vaadin.client.ServerConnector;
    import com.vaadin.client.extensions.AbstractExtensionConnector;
    import com.vaadin.client.ui.VTextField;
    import com.vaadin.shared.ui.Connect;
    
    @Connect(NumberField.class)
    public class NumberFieldConnector extends AbstractExtensionConnector {
                private static final long serialVersionUID = -737765038361894693L;
    
    private VTextField textField;
    private KeyPressHandler keyPressHandler = new KeyPressHandler() {
        @Override
        public void onKeyPress(KeyPressEvent event) {
            if (textField.isReadOnly() || !textField.isEnabled()) {
                return;
            }
            int keyCode = event.getNativeEvent().getKeyCode();
            switch (keyCode) {
            case KeyCodes.KEY_LEFT:
            case KeyCodes.KEY_RIGHT:
            case KeyCodes.KEY_BACKSPACE:
            case KeyCodes.KEY_DELETE:
            case KeyCodes.KEY_TAB:
            case KeyCodes.KEY_UP:
            case KeyCodes.KEY_DOWN:
            case KeyCodes.KEY_SHIFT:
                return;
            }
            if (!isValueValid(event)) {
                textField.cancelKey();
            }
        }
    };
    
    @Override
    protected void extend(ServerConnector target) {
        textField = (VTextField) ((ComponentConnector) target).getWidget();
        textField.addKeyPressHandler(keyPressHandler);
    }
    
    private boolean isValueValid(KeyPressEvent event) {
        String newText = getFieldValueAsItWouldBeAfterKeyPress(event.getCharCode());
        try {
            parseValue(newText);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    
    protected long parseValue(String value) {
        return Long.valueOf(value);
    }
    
    private String getFieldValueAsItWouldBeAfterKeyPress(char charCode) {
        int index = textField.getCursorPos();
        String previousText = textField.getText();
        StringBuffer buffer = new StringBuffer();
        buffer.append(previousText.substring(0, index));
        buffer.append(charCode);
        if (textField.getSelectionLength() > 0) {
            buffer.append(previousText.substring(index + textField.getSelectionLength(),
                    previousText.length()));
        } else {
            buffer.append(previousText.substring(index, previousText.length()));
        }
        return buffer.toString();
    }
    }
    

To use the code above you need to add it to your current widget set. Afterwards the use of this is as follows:

TextField field = new TextField();
NumberField.extend(field);
Ahmed Ashour
  • 4,209
  • 10
  • 29
  • 46
  • 1
    To add it to the widgetset, the Connector class should be in a folder called "client" at the same level as the custom widgetset file (MyWigetSet.gwt.xml for example). To use a custom widgetset, this must be declared as an init-param in the servlet. – enkara Feb 26 '16 at 13:19
4

In Vaadin 7, you can use a TextField and set a validator to allow only numbers:

TextField textField;
textField.addValidator(new RegexpValidator("[-]?[0-9]*\\.?,?[0-9]+"), "This is not a number!");

Change the regex to fit your needs. Remember that still is handling Strings and therefore you still need to convert the returning value of the TextField:

Long.parseLong(textField.getValue())
King Midas
  • 1,143
  • 4
  • 23
  • 41
3

With Vaadin 8, you can use Binder:

Binder<YouBean> binder = new Binder<>();
binder.forField(textField)
      .withConverter(new StringToIntegerConverter("Must be Integer"))
      .bind(YouBean::getter, YouBean::setter);
binder.setBean(bean);  //optional
Ahmed Ashour
  • 4,209
  • 10
  • 29
  • 46
  • I migrated from the TextField from vaadin 7 to 8, and it worked using this: Binder without the 'bind' call. There is no comment on migrating this from 7 to 8 and your comment should be in the official doc. – Sebastian D'Agostino Jul 08 '20 at 11:13
2

A TextField is a component that always has a value of type String. When binding a property of another type to a text field, the value is automatically converted if the conversion between the two types is supported.

public class MyBean {
    private int value;

    public int getValue() {
        return value;
    }

    public void setValue(int integer) {
        value = integer;
    }
}

The property named "value" from a BeanItem constructed from MyBean will be of type Integer. Binding the property to a TextField will automatically make validation fail for texts that can not be converted to an Integer.

final MyBean myBean = new MyBean();

BeanItem<MyBean> beanItem = new BeanItem<MyBean>(myBean);

final Property<Integer> integerProperty = (Property<Integer>) beanItem
        .getItemProperty("value");
final TextField textField = new TextField("Text field", integerProperty);

Button submitButton = new Button("Submit value", new ClickListener() {
    public void buttonClick(ClickEvent event) {
        String uiValue = textField.getValue();
        Integer propertyValue = integerProperty.getValue();
        int dataModelValue = myBean.getValue();

        Notification.show("UI value (String): " + uiValue
                + "\nProperty value (Integer): " + propertyValue
                + "\nData model value (int): " + dataModelValue);
     }
});

addComponent(new Label("Text field type: " + textField.getType()));
addComponent(new Label("Text field type: " + integerProperty.getType()));
addComponent(textField);
addComponent(submitButton);

With this example, entering a number and pressing the button causes the value of the TextField to be a String, the property value will be an Integer representing the same value and the value in the bean will be the same int. If e.g. a letter is entered to the field and the button is pressed, the validation will fail. This causes a notice to be displayed for the field. The field value is still updated, but the property value and the bean value are kept at their previous values.

Tiago
  • 2,219
  • 2
  • 21
  • 38
Janny
  • 623
  • 8
  • 30
2

This is an update (2017 with vaadin 8) for @raffael answer:

public class DoubleField extends TextField implements ValueChangeListener<String>  {

public String lastValue;

public DoubleField() {
    setValueChangeMode(ValueChangeMode.EAGER);
    addValueChangeListener(this);
    lastValue="";
}

@Override
public void valueChange(ValueChangeEvent<String> event) {
    String text = (String) event.getValue();
    try {
        new Double(text);
        lastValue = text;
    } catch (NumberFormatException e) {
        setValue(lastValue);
    }

}
Ahmed Ashour
  • 4,209
  • 10
  • 29
  • 46
jdamianb
  • 76
  • 5
0

NumberField is available for Vaadin 7 and 8 by now.

T-Gergely
  • 452
  • 5
  • 11