vaadin / flow-components

Java counterpart of Vaadin Web Components
100 stars 66 forks source link

Dialog won't open after redirect #6095

Open mvysny opened 6 months ago

mvysny commented 6 months ago

Description

I have a Spring Security in place which shows LoginRoute when no user is logged in. The LoginRoute is just a simple Div which opens a LoginDialog on attach. The problem is that the dialog won't open in certain cases. More precisely:

  1. The dialog opens when you navigate straight for LoginRoute
  2. The dialog won't open when you navigate to a protected view first, which then redirects to LoginRoute

Debugging the issue I discovered that in Dialog.ensureAttached(), in the second use-case the location change trigger is PROGRAMMATIC, the UI registration listener gets unregistered and the dialog is never attached.

Expected outcome

The dialog should be shown regardless of what kind of navigation chain occurred.

Minimal reproducible example

Create a route which redirects to another route, which then opens a dialog.

Steps to reproduce

I don't have a Spring-Boot+Security project but the issue is easily reproducible in a simpler project:

  1. git clone https://github.com/mvysny/vaadin-simple-security-example
  2. Edit LoginRoute and add the following lines to the constructor:

        final Dialog dlg = new Dialog();
        dlg.setHeaderTitle("Won't be shown");
        dlg.add(new Span("Foo"));
        dlg.setCloseOnOutsideClick(false);
        dlg.open();
  1. Start the app. The app navigates to WelcomeRoute which is protected by security and therefore a redirect to LoginRoute occurs. The LoginRoute tries to open the dialog but the dialog won't open.

This bug is also unit-testable easily.

Environment

Vaadin version(s): 24.3.6 OS: Ubuntu 23.10

Browsers

No response

mvysny commented 6 months ago

Even simpler steps to reproduce: paste this to the Skeleton Starter:

@Route
public class MainView extends VerticalLayout implements BeforeEnterObserver {
    @Override
    public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
        beforeEnterEvent.forwardTo(SecondView.class);
    }

    @Route("second")
    public static class SecondView extends VerticalLayout {
        public SecondView() {
            new Dialog("Foo").open();
            add(new Span("Look Ma, no dialog. Refresh the page to see the dialog"));
        }
    }
}
mvysny commented 6 months ago

Workaround is to add the Dialog manually to the UI:

@Route
public class MainView extends VerticalLayout implements BeforeEnterObserver {
    @Override
    public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
        beforeEnterEvent.forwardTo(SecondView.class);
    }

    @Route("second")
    public static class SecondView extends VerticalLayout {
        public SecondView() {
            final Dialog dlg = new Dialog("Foo");
            dlg.open();
            UI ui = UI.getCurrent();
            ui.beforeClientResponse(ui, context -> {
                        if (dlg.getElement().getNode().getParent() == null
                                && dlg.isOpened()) {
                            ui.addToModalComponent(dlg);
                            ui.setChildComponentModal(dlg, dlg.isModal());
                        }
                    });
            add(new Span("Look Ma, no dialog. Refresh the page to see the dialog"));
        }
    }
}

I wonder what breaks.... :thinking: