crabe
crabe

Reputation: 332

How to create a real time validator on a text field in Vaadin?

I am currently using Vaadin 7.3+ and want to validate in a text field as the user types in real time.

This is what I tried so far:

textField.setTextChangeEventMode(TextChangeEventMode.LAZY);
textField.setNullRepresentation("");

textField.addTextChangeListener(new FieldEvents.TextChangeListener() {
    @Override
    public void textChange(FieldEvents.TextChangeEvent event) {
        for (Validator v : textField.getValidators()) {
            try {
                v.validate(event.getText());
            } catch (InvalidValueException e) {
                log.warn("validation error: " + e.getMessage() + " and value was: {}", event.getText());
            }
        }
    }
});

The problem is that although all the validators are being executed and validation is being done the red error indicator is not rendered until the focus leaves the field, i.e. the user hits enter or clicks somewhere else. I tried adding textField.markAsDirty but this did not work. Does anyone know of a solution to this problem? Or of a better solution in general for creating a real time validator on a text field?

Thanks in advance for your time and input :-)

Upvotes: 4

Views: 6047

Answers (4)

wirebug
wirebug

Reputation: 78

Take a look in the Vaadin Documentation. If I get you right it should be fine to set your field in immediate mode. Sometimes it is recommend to allow null values to avoid unnecessary warnings.

TextField field = new TextField("Name");
 field.addValidator(new StringLengthValidator(
"The name must be 1-10 letters (was {0})",
1, 10, true));
field.setImmediate(true);
field.setNullRepresentation("");
field.setNullSettingAllowed(true);

Upvotes: 0

mstahv
mstahv

Reputation: 1934

Validators in core the Vaadin are not designed to work while typing, which is a shame for a RIA framework. This will hopefully be fixed in upcoming version. Making it work well today with core components is bit tricky, but doable. Your own solution probably has some UX issues if there is some latency between your server and the client - the cursor might jump into unintended place if user starts to retype again while validator is executed. I have worked on this a lot in Viritin add-on. By using its AbstractForm (or raw MBeanFieldGroup) together with MTextField this should work pretty well and without any configuration. You can try that solution with e.g. this example.

Upvotes: 2

crabe
crabe

Reputation: 332

This straight forward workaround solution seems to work fine although it is quite inelegant.

textField.setTextChangeEventMode(TextChangeEventMode.LAZY);
textField.setNullRepresentation("");

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

            // workaround cursor position problem
            textField.setCursorPosition(event.getCursorPosition());

            textField.validate();
            } catch (InvalidValueException e) {
                log.warn("validation error: " + e.getMessage() + " and value was: {}", delegate.getValue());
            }
    }
});

Upvotes: 1

cfrick
cfrick

Reputation: 37008

the problem here is, that the event sends the text, but does not actually modify the value of the input. the easiest way to go around this would be setting the value. e.g.

addTextChangeListener(new FieldEvents.TextChangeListener() {
    @Override
    void textChange(FieldEvents.TextChangeEvent event) {
        final textField = event.source as TextField
        textField.value = event.text
    }
})

this would just trigger the change of the field and also the validators and all will go down to the client as expected.

edit

as you stated in the comments, the cursor pos should be kept. You can just validate the text from the event with whatever means you need. Key point here is, to just set the componentError of the field to get the error down the line for the field.

@Override
void textChange(FieldEvents.TextChangeEvent event) {
    final tf = event.source as TextField
    try {
        tf.validate(event.text) // this works in groovy!  not java.
        tf.setComponentError(null)
    }
    catch (InvalidValueException e) {
        tf.setComponentError(new SystemError(e))
    }
}

Upvotes: 1

Related Questions