3

I have a JSF page that loads the properties of an object (for which the id is passed in the URL). The loading can last more seconds, so I would like to display a wait/busy indicator or a "Loading..." message.

This is done using "viewAction"

<f:metadata>
    <f:viewAction action="#{myBean.loadParams}" />
</f:metadata>

Is there a simple way to accomplish this goal? I'm using Primefaces.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Neo
  • 1,337
  • 4
  • 21
  • 50

2 Answers2

4

PrimeFaces has already a component ready for that: the <p:outputPanel deferred="true">. You only need to make sure that the #{heavyBean} is only referenced in a component (and thus definitely not in a tagfile like <c:xxx> for the reasons explained here) within the <p:outputPanel> and not somewhere else.

...
#{notHeavyBean.property}
...

<p:outputPanel deferred="true">
    ...
    #{heavyBean.property}
    ...
</p:outputPanel>

...
#{anotherNotHeavyBean.property}
...

Then you can do the heavy job in its @PostConstruct method. Do the job you originally did in <f:viewAction> there in the @PostConstruct.

@Named
@ViewScoped
public class HeavyBean implements Serializable {

    @PostConstruct
    public void init() {
        // Heavy job here.
    }

    // ...
}

If you need to access properties of other beans, simply @Inject those beans in the HeavyBean. E.g. in case you needed the ID view param:

<f:viewParam name="id" value="#{notHeavyBean.id}" />

@Inject
private NotHeavyBean notHeavyBean; // Also @ViewScoped.

@PostConstruct
public void init() {
    Long id = notHeavyBean.getId();
    // Heavy job here.
}

The <p:outputPanel> already comes with an animated gif. You can easily customize it via CSS.

.ui-outputpanel-loading {
    background-image: url("another.gif");
}
Kukeltje
  • 12,223
  • 4
  • 24
  • 47
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    Please note that a GIF might not be the best choice for a loader image. See [How to remove borders from loading gif on a dark background?](https://stackoverflow.com/questions/56505619/how-to-remove-borders-from-loading-gif-inside-dialog) – Jasper de Vries Jun 12 '19 at 10:43
  • WOW. Thx a lot! I have tried it: the only problem is that the page is an "entry point" of a complex view, build with jstl tags (that I cannot remove), and those tags are causing problems as you expected. I have tried adding a remotecommand in the deferred pane with autorun="true" and updating the component. In this way it seems to work, but I'm not sure it is a good solution (performance seems to get worse). – Neo Jun 14 '19 at 16:13
0

I would like to propose also this simple approach:

  • one "landing" page (the page where we first navigate in) with a wait indicator and an autoRun remoteCommand with an event that read the parameter "param" from the URL and save it in the bean.
  • the remoteCommand does a redirect to another page (where the long-running method loadParams is executed) In this way the wait indicator is shown until the second page is ready to be displayed.

Do you see any weaknesses?

Here the landing page:

<!DOCTYPE html>
<html
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.org/ui">
<h:head>
...
</h:head>
<f:metadata>
     <f:event type="postAddToView" listener="#{notHeavyBean.readProperty}" />
     <f:viewParam name="param"/>
</f:metadata>
<h:body>
  <p:outputPanel layout="block">
      <i class="fa fa-circle-o-notch fa-spin layout-ajax-loader-icon" aria-hidden="true" style="font-size: 40px;position: relative;top: 50%;left: 50%;"></i>
  </p:outputPanel>


<h:form>
    <p:remoteCommand action="#{notHeavyBean.redirect}" autoRun="true"/>
</h:form>


</h:body>

Neo
  • 1,337
  • 4
  • 21
  • 50