Notication not displayed on first attempt, but on second try displayed twice

On a form which calls a service when clicking a button,
a Notification should be displayed to show either the success or failure of the async backend action.

When clicking the button for the first time, nothing is displayed. I do not see any change in the DOM and no additional http request/responses for this notification.
The System.out message is displayed, so the codeblock is executed.

Clicking the button for a second time, when the backend action completes, first the notification of the first execution is displayed and, after this one has disappeared, the notification of the second execution is displayed.
The DOM is modified for each notification and there are additional http request/responses for the notification.

This is consistent. So, again first click no notification, second click 2 notification, etc.

What am I doing wrong?

In other parts of the application, notification are working normally.
The code must run in ui.access(() -> ...)); otherwise it fails in Notification.setOpened(bool) since UI.getCurrent() then returns null.

Here is a simplified version of the form code:

public class StartRegistrationForm extends FormLayout {

    private final MyService myService;

    public StartRegistrationForm(MyService myService) {
        this.myService = myService;
    }

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        super.onAttach(attachEvent);

        var nameField = new TextField("Name");

        add(new Text("Start registration process"));
        add(nameField);

        add(new Button("Start registration", _ -> {

            attachEvent.getUI().getPage().fetchCurrentURL(url -> {
                myService.startRegistrationProcess(url)
                        .subscribe().with(
                                registration -> {
                                    doInUi(() -> {
                                        var notification = new Notification(
                                                "Registered successfully",
                                                5000,
                                                Notification.Position.MIDDLE
                                        );
                                        notification.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
                                        notification.open();
                                        System.out.println("Registered");
                                    });
                                }, error -> {
                                    doInUi(() -> {
                                        var notification = new Notification(
                                                STR."Registration failed: \{error.getMessage()}",
                                                5000,
                                                Notification.Position.MIDDLE);
                                        notification.addThemeVariants(NotificationVariant.LUMO_ERROR);
                                        notification.open();
                                        System.out.println("Registration failed");
                                    });
                                });
            });
        }));
        setResponsiveSteps(new FormLayout.ResponsiveStep("0", 1));
    }

    private void doInUi(Runnable runnable) {
        getUI().ifPresent(ui -> ui.access(() -> runnable.run()));
    }
}

Did you add @Push like described here? Enabling Push in Your Application

Did you check that the UI is present? Typically the optional won’t return anything if called from another thread. So you normally would want to store the UI and pass it to your asynchronous call’s constructor.

1 Like