WaTho
WaTho

Reputation: 41

Event on view rendering finished

After navigating to a new view i want to start a background thread, which does some validation and reports changes to my view. I am building the ui and starting the thread on AfterNavigationObserver#afterNavigation.

Most of the time this works, but when the background thread finishes too fast it can't update the view cause the rendering is not finished and the component to update is not visible yet. The possible solution would be to start the background thread after the rendering is finished and the view is visible in the browser. What is the recommended way to do this? I looked through the APIs but can't find a matching event.

Using vaadin-flow 14.8.

In the following minimal example the second accordion panel should be opened when the task finished. It works on my pc mostly with a timeout of 500 ms or greater. Going below this value or removing the sleep results in not opening the second accordion.

The view:

import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.accordion.Accordion;
import com.vaadin.flow.component.accordion.AccordionPanel;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.router.AfterNavigationEvent;
import com.vaadin.flow.router.AfterNavigationObserver;
import com.vaadin.flow.router.Route;
import de.fs_aut.ddm.desktop.bgtasks.TimeConsumingTask;
import org.springframework.beans.factory.annotation.Autowired;

@Route("debug")
@Push
public class DebugView extends VerticalLayout implements AfterNavigationObserver {

  @Autowired
  private TimeConsumingTask task;

  private UI ui;
  private Accordion acc;
  private AccordionPanel firstPanel;
  private AccordionPanel secondPanel;

  @Override
  public void afterNavigation(AfterNavigationEvent event) {
    removeAll();
    acc = new Accordion();
    firstPanel = new AccordionPanel("First task", new Label("first task in progress"));
    secondPanel = new AccordionPanel("second task", new Label("second task in progress"));
    acc.add(firstPanel);
    acc.add(secondPanel);
    add(acc);
    startValidation();
  }

  public void startValidation() {
    ui = UI.getCurrent();
    task.sleep().addCallback(result -> {
      ui.access(() -> {
        acc.open(secondPanel);
      });
    }, error -> {
    });
  }
}

The async task:

import java.time.LocalDateTime;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFuture;

/**
 * Testdummy. Does nothing but waits.
 *
 */
@Service
public class TimeConsumingTask {

  @Async
  public ListenableFuture<String> sleep() {
    try {
      Thread.sleep(500);
    } catch (final InterruptedException e) {
      e.printStackTrace();
    }
    return AsyncResult.forValue("Validation finished at " + LocalDateTime.now().toString());
  }
}

Upvotes: 1

Views: 678

Answers (2)

Oliver
Oliver

Reputation: 1645

Knoobie's answer should be checked, but (if I remember correctly) StackOverflow guidelines prefers code over a link to the same.

Every Component the onAttach(DetachEvent) method. From the Javadoc:

`protected void onAttach(AttachEvent attachEvent)

Called when the component is attached to a UI.

The default implementation does nothing.

This method is invoked before the AttachEvent is fired for the component.

Parameters:

attachEvent - the attach event

You can override this method in any Component:

public class MyComponent extends VerticalLayout {
  @Override
  protected void onAttach(AttachEvent event) {
    System.out.println("I attached: " + event);
  }
}

Component also implements AttachNotifier, which offers the this method:

public class MyComponent extends VerticalLayout {
  public MyComponent() {
    addAttachListener(event -> System.out.println("I attached: " + event));
  }
}

Note the docs quoted above! The onAttach method is called before the event.

Another approach is from JavaScript to wait for the v-loading-indicator's first child to have display: none.

Upvotes: 0

Knoobie
Knoobie

Reputation: 2064

You could try to overwrite and implement onAttach on your route. See https://vaadin.com/docs/v14/flow/creating-components/tutorial-component-lifecycle-callbacks for more details.

Upvotes: 1

Related Questions