ProgressIndicator and navigating from another thread

Hi,
I needed to disable all buttons of a form when comitting it and I solved it performing the commit in a separate thread.
This is what I have done:

[code]
sendButton.setDisableOnClick(true);
sendButton.addClickListener(new Button.ClickListener()
{
private static final long serialVersionUID = 1L;

@Override
public void buttonClick(ClickEvent event)
{
cancelButton.setEnabled(false);
new Performer().start();
}
});

class Performer extends Thread
{
@Override
public void run ()
{
try {
getUI().access( new Runnable()
{
@Override
public void run ()
{
commitForm();
}
} );
} catch ( Exception e ) {
e.printStackTrace();
}
}
}
[/code]I have two problems now:
1 - I need to navigate to another view when the commiting is finished, but if I do it from commitForm I get an Exception because I’m not in the UI thread. If I do it in the UI thread I have to wait for the other thread to finish and that makes the cancelButton not to be disabled.
2 - The default progress indicator doesn’t show. I would like to show the same progress indicator than in the other places in the web for continuity reasons. Is there a way to make it appear?

Thanks for your help

Ok, I have been able to make the default progress indicator by adding my own like described in the Book of Vaadin (https://vaadin.com/book/-/page/components.progressbar.html) and then hiding it.

But I still don’t know how can I navigate to another page when commitForm() is finished.

I don’t know how to solve the problem of navigating to another page from another thread but I solved my problem in a much simpler way. Although I had push enabled, the cancelButton wouldn’t be disabled before the commitForm() was finished, that’s why I made another thread.
But I realized that just pushing the changes manually makes the trick. I don’t need a second thread and I have no more problems with the progressbar or navigating in the other thread.
So I do something like this:

sendButton.setDisableOnClick(true);
sendButton.addClickListener(new Button.ClickListener()
{
   private static final long serialVersionUID = 1L;
   @Override
   public void buttonClick(ClickEvent event)
   {
      cancelButton.setEnabled(false);
      MyUI.getCurrent().push();

      commitForm();

      MyUI.getCurrent.getNavigator.navigateTo("MyView");
   }
});

Hi Enkara,

It seems you have several questions.

First of all about the push. If you rely on automatic push, all your changes, i.e. disable a button, will be pushed to the client when the session lock is released, with other words when the buttonClick call ends and all your code is executed.

Manual push fires the changes immediattely, so in your second example it’s natural to get your cancel button disabled right away.

Related to your first example: button click starts with cancelButton set to disabled. But only the server button object (the Button instance) is disabled. No push was fired yet, so in the browser, the button is enabled. Then a thread is started. After this normally the push should occur (but it didn’t - most likely due to the UI.access call). More interesting about what the Performer thread does is that it’ll lock again the session just to perform business logic most likely, so any UI changes are locked until it finishes. Never perform business logic on the event/UI thread.

The way to deal with a form commit is as follow:

  • read your fields right away on the buttonClick.
  • perform any calculations/transactions using those values on a separate thread.
  • only update the UI from the event/UI thread, in Vaadin’s case using UI.access (don’t do any business logic in UI.access, it’ll only block the user interface).

Also, you can leave the Cancel button enabled and in case user press it in the middle of your commit, you can just abort that commit.

Then I tried to simulate your functionality so hopefully you can get some ideas from it, or if I didn’t understand your issue right please let me know. This is what I think it should be:

@Push
public class NavigationForm extends UI {

    public static final String MAIN_VIEW_ID = "";
    public static final String FORM_VIEW_ID = "form";

    @WebServlet(value = "/*", asyncSupported = true)
    @VaadinServletConfiguration(productionMode = false, ui = NavigationForm.class)
    public static class Servlet extends VaadinServlet {
    }

    private Navigator navigator;

    @Override
    protected void init(VaadinRequest request) {
        navigator = new Navigator(this, this);
        navigator.addView(MAIN_VIEW_ID, new MainView());
        navigator.addView(FORM_VIEW_ID, new FormView());
    }

    private class MainView extends VerticalLayout implements View {

        public MainView() {
            addComponent(new Label("Main view"));

            Button openFormButton = new Button("Open form");
            openFormButton.addClickListener(new ClickListener() {

                @Override
                public void buttonClick(ClickEvent event) {
                    navigator.navigateTo(FORM_VIEW_ID);
                }
            });
            addComponent(openFormButton);
        }

        @Override
        public void enter(ViewChangeEvent event) {
        }
    }

    private class FormView extends FormLayout implements View {

        private Button sendButton;
        private Button cancelButton;

        public FormView() {
            final TextField field1 = new TextField("First Name: ");
            final TextField field2 = new TextField("Last Name: ");

            addComponent(field1);
            addComponent(field2);

            sendButton = new Button("Send");
            sendButton.setDisableOnClick(true);

            cancelButton = new Button("Cancel");

            sendButton.addClickListener(new ClickListener() {

                @Override
                public void buttonClick(ClickEvent event) {

                    // Retrieve any data from UI fields
                    Business business = new Business(field1.getValue(), field2
                            .getValue());

                    cancelButton.setEnabled(false);

                    commitForm(business);
                }
            });
            addComponent(sendButton);

            cancelButton.addClickListener(new ClickListener() {

                @Override
                public void buttonClick(ClickEvent event) {
                    navigator.navigateTo(MAIN_VIEW_ID);
                }
            });
            addComponent(cancelButton);
        }

        @Override
        public void enter(ViewChangeEvent event) {
            sendButton.setEnabled(true);
            cancelButton.setEnabled(true);
        }

        protected void commitForm(final Business business) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    business.commit();

                    access(new Runnable() {
                        @Override
                        public void run() {
                            navigator.navigateTo(MAIN_VIEW_ID);
                            Notification.show("My name is: "
                                    + business.getFullName());
                        }
                    });
                }
            }).start();
        }

    }

    private class Business {

        private String firstName;
        private String lastName;

        private String fullName;

        public Business(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        protected void commit() {
            // Commit/transaction starts.
            fullName = firstName + " " + lastName;

            // Simulate a heavy operation.
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
            }
            // Commit/transaction ends.
        }

        public String getFullName() {
            return fullName;
        }
    }

}

What remains here to add is the ProgressIndicator.

Have a great weekend,
Bogdan.