Search code examples
vaadinvaadin-flowvaadin23

Vaadin 23: How to sync field values within ShortcutEventListener (Ctrl + S)?


Intention: In an existing Vaadin 23 form I'd like to add a shortcut Ctrl+S to trigger a save action.

Code: This is a reduced (but running) code:

@Route("sandbox")
public class SandboxView extends VerticalLayout {

public SandboxView() {
    TextField textField = new TextField();
    Shortcuts.addShortcutListener(this, new ShortcutEventListener() {
        @Override
        public void onShortcut(ShortcutEvent event) {
            System.out.println("Saving value ... "+textField.getValue());
        }
    }, Key.KEY_S, KeyModifier.CONTROL).listenOn(this);
    this.add(textField);
}

Problem: When the user types some text into the Textfield and then hits Ctrl+S (without escaping the focus from the TextField), then textField.getValue() within the ShutcutEventListener returns the value that was in the TextField bevore the user focused the TextField.

Workarounds:

  • Workaround A is that the user moves the focus out of the TextField and then hits Ctrl+S. Works but that is error-prone because not every user will remember every time to do so.
  • Workaround B is to set the valueChangeMode to EAGER: textField.setValueChangeMode(ValueChangeMode.EAGER);. Works fine but this would cause more traffic and more server load (because there are some valueChangeListeners at every field that do validation and some recalculation of some formulas) - so this is not the way I want to go.

Question: Is there a way to sync all fields in the form to the backend within the ShortcutEventListener?

What I tried:

  • The working workarounds above
  • Calling textfield.blur() triggers the JavaScript "blur" method, but the synchronization happens after the ShortcutEventListener execution.
  • Using a Binder and explicitly call binder.writeBeanIfValid(binder.getBean()); in the ShortcutEventListener. The bean still contains the old TextField value.

Solution

  • One option is to trigger blur in client side and and read the field in then call back of the call. This will ensure that field is not read before, but after the roundtrip.

        TextField field = new TextField();
        field.setValueChangeMode(ValueChangeMode.ON_BLUR);
        Shortcuts.addShortcutListener(field, e -> {
            field.getElement().executeJs("this.blur()").then(result -> Notification.show("field: " + field.getValue()));
        }, Key.ENTER).listenOn(field);