how do I create a textfield which supports numbers only in vaadin
Asked Answered
S

7

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.

Splutter answered 17/6, 2013 at 9:57 Comment(0)
Z
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);
    }
}
}
Zephyr answered 21/6, 2013 at 13:26 Comment(4)
thanks dude. It works fine. I added some null check and empty check and handled them.Splutter
Working but when typing lots of characters quickly you can still enter unwanted contentSelfwill
Its generally considered bad practice to use exceptions for control flow. See c2.com/cgi/wiki?DontUseExceptionsForFlowControl, programmers.stackexchange.com/questions/189222/… or #729879.Krupp
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.Championship
D
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);
Deloresdeloria answered 26/9, 2013 at 17:15 Comment(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.Goddart
R
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())
Rigsdaler answered 27/10, 2015 at 15:58 Comment(0)
E
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
Elsie answered 28/3, 2018 at 14:58 Comment(1)
I migrated from the TextField from vaadin 7 to 8, and it worked using this: Binder<TextField> without the 'bind' call. There is no comment on migrating this from 7 to 8 and your comment should be in the official doc.Biological
A
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.

A1 answered 17/6, 2013 at 10:4 Comment(0)
G
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);
    }

}
Gravante answered 18/6, 2017 at 12:27 Comment(0)
D
0

NumberField is available for Vaadin 7 and 8 by now.

Decapod answered 3/8, 2017 at 12:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.