1

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();
        }
    }
}
Vicente
  • 35
  • 5

1 Answers1

2

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.

kscherrer
  • 5,486
  • 2
  • 19
  • 59
  • Hi kscherrer. Thanks for your quick response. I have followed your instructions in my project, but unfortunately the problem persists. I even have created a Vaadin "my-starter-project" with just one class with your code, but the result is again the same: it opens the dialog once the method has ended its execution. I am using Eclipse and Vaadin 10+ project. When I create the project, the Tech stack is Spring boot. Any idea? Thanks again. – Vicente Jul 10 '20 at 11:07
  • can you show the code of your view? Maybe I see the problem then. The code as I wrote it in my answer should work. – kscherrer Jul 10 '20 at 11:17
  • Sure, I have edited my original question. I am testing your code on a project that only contains one class. Thanks. – Vicente Jul 10 '20 at 12:29
  • I just tried the code out myself and it doesn't work indeed. I am perplexed! Apparently, a clicklistener locks the ui completely until finished, therefore the `ui.access` is delayed until it can get a lock on it itself. A solution is to put the heavy operations in an asynchronous/background thread, so the clicklistener finishes quickly, and then you perform ui updates with `ui.access` from the background thread. – kscherrer Jul 10 '20 at 13:17
  • I have edited my answer accordingly, and I have tested it this time too - it works for me. I really didn't know that a clicklistener locks the ui! I think I gave wrong advice many times here if that is really the case.. – kscherrer Jul 10 '20 at 13:31
  • 1
    It works! Thanks for your research and help. I am very grateful. – Vicente Jul 13 '20 at 08:19