Search code examples
vaadinvaadin10

Vaadin Dialog is opened after a method execution instead of before


When I click a button, I want to do 2 things: first, to open a Dialog, and second, to execute a method (which is in a jar file). The problem is that, when I click the button, first it is executed the method and after that, the Dialog is opened. The code is something like this:

button.addClickListener(event -> start()); 

public void start() { 
        dialog.open(); //Second, it opens the Dialog
        methodInJar(); //First it executes this method
    }

Note: The same thing happens regardless of whether it is a Dialog, a Notification or any change in the interface.

Does anyone know where is the problem?

Thank you.

PD. This is the whole class:

package com.example.test;

import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.router.Route;

@Route("myView")
@Push
public class MyView extends VerticalLayout {

    private UI ui;
    private Dialog dialog = new Dialog();

    public MyView(){

        addAttachListener(event -> {
            this.ui = event.getUI();
        });
        add(new Button("some button", click -> start()));
    }

    private void start(){
        this.ui.access(() -> {
            dialog.open();
        });
        methodInJarTest();
    }
    
    private void methodInJarTest() {
        try {
            System.out.println("JAR test - 10 seconds pause");
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Solution

  • Any UI changes are performed after the whole roundtrip to the server has finished, aka after the buttons clickListener has completely finished in your case. And because during the whole clicklistener, the UI is locked, you can't even do a ui.access during the click listener, because that needs a UI-lock as well.

    What you can do here is to let the clicklistener start a new Thread, within which you perform the heavy liftings. This means that the clicklistener is finished very quickly and therefore releases its lock on the UI. We can now perform ui.access(...) calls from within the background Thread which is still running.

    Here is how your code would look like

    @Route("myView")
    @Push
    public class MyView extends VerticalLayout {
    
        private UI ui;
        private Dialog dialog = new Dialog();
    
        public MyView(){
            // save the ui instance when the view is attached for later reference
            addAttachListener(event -> {
                this.ui = event.getUI();
            });
            add(new Button("some button", click -> start()));
        }
    
        private void start(){
            new Thread(() -> {
                // UI.getCurrent() would return null in a new Thread, that's why we saved the ui instance during attach of the view.
                ui.access(() -> dialog.open());
                methodInJarTest();
            }).start();
        }
    
        private void methodInJarTest() {
            try {
                System.out.println("JAR test - 10 seconds pause");
                Thread.sleep(10000);
                ui.access(() -> dialog.add(new Span("methodInJar completed")));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    Docs:
    @Push
    Asynchronous Updates

    In general it can be said that you should perform any task that could take a while inside a background thread, to avoid locking the UI.