Unexpected behaviour of TextField

We want to use a TextField that can trigger a save event by using the Enter key. Our first approach lookes the following way:

var input = new TextField();
var output = new TextField();
Shortcuts.addShortcutListener(input, () -> {
  output.setValue("Value (shortcut): "+input.getValue());
}, Key.ENTER);

add(input, output);

This did not work - the TextField’s value was always empty.

When a button is used to get the value, everything works:

var input = new TextField();
var output = new TextField();
var button = new Button();
button.addClickListener(e -> {
  output.setValue("Value (button): "+input.getValue());
});
add(input, button, output);

Last but not least we got it working by using ValueChangeMode.EAGER:

var input = new TextField();
input.setValueChangeMode(ValueChangeMode.EAGER);
var output = new TextField();
Shortcuts.addShortcutListener(input, () -> {
  output.setValue("Value (shortcut): "+input.getValue());
}, Key.ENTER);

add(input, output);

Why does clicking a Button commits the TextField before triggering the event while the ShortCutListener does not? And is there any better way than using the EAGER mode?

Christian Hufgard:
Why does clicking a Button commits the TextField before triggering the event while the ShortCutListener does not? And is there any better way than using the EAGER mode?

It’s because of how JavaScript events are used to update input values. The value property of the <vaadin-text-field> (or even just a regular HTML input) in the browser is only changed when a blur even occurs - that is, when the text field loses focus. If you click a button, you will lose focus from the text field, so a value change will happen. If you have a shortcut listener on Enter, the text field never loses focus, so the value is not updated in the browser. Thus the value change also can’t be sent to the server, there’s nothing to update yet.

One possible workaround is to programmatically focus another component in the shortcut listener, e.g. using the Element API on the server: button.getElement().callJsFunction("focus");. This should now trigger the value change of the TextField.

Hi Olli,

thanks for the blazing fast response!

Focussing the button did not work but this code does:

Shortcuts.addShortcutListener(tag, () -> {
  tag.getElement().callJsFunction("blur");
}, Key.ENTER);
tag.addValueChangeListener(e -> listener.onComponentEvent(new ClickEvent<>(this)));

Strange; I tried this and it seems to work for me:

        Button button = new Button("something");
		TextField tf = new TextField("Enter shortcut");
        tf.addValueChangeListener( e-> {
            Notification.show("tf value: " + e.getValue());
        });
        Shortcuts.addShortcutListener(tf, e -> {
           button.getElement().callJsFunction("focus");
        }, Key.ENTER);

Anyway, good to hear that you got it working.

Ah, think I found my mistake. I did not use the ValueChangeListener when I tried your idea. But in the end I prefere to use “blur” on the TextField instead of adding a new invisible button. Thanks for pointing me to the right direction.