5
votes

Consider a simple h:outputText component:

<h:outputText value="#{myBean.myValue}"/>

How can I lazy load that value after the page has been rendered, and display custom 'ajax loading' icon instead of the value while this is being done?

I am using PrimeFaces 3.5 in my project so any PF-specific implementation will be welcome.

3

3 Answers

6
votes

A suggest to do this by calling remoteCommand after on page load (it is done by setting autoRun attribute to true) and update your outputText.

private String myValue;

// getter and setter

public void initMyValue() {
  // init myValue
}

On page you should have ajaxStatus component for viewing loading image, and your outputText. Also there should be p:remoteCommand component:

<p:ajaxStatus style="width:16px;height:16px;" id="ajaxStatusPanel">  
  <f:facet name="start">  
    <h:graphicImage value="ajaxloading.gif" />  
  </f:facet>
  <f:facet name="complete">  
    <h:outputText value="" />
  </f:facet>  
</p:ajaxStatus>

<h:outputText id="myText" value="#{myBean.myValue}"/>

<p:remoteCommand autoRun="true" actionListener="#{myBean.initMyValue}" update="myText"/>

EDIT: I supposed that you want to lazy load value of outputText because it contains some long running calculations, but if you want to completely deffer rendering of outputText first add boolean property in your backing bean, and set this property to true at the end of initMyValue method:

private boolean loaded;

// getter and setter

public void initMyValue() {
  // init myValue
  loaded = true;
}

on the page reorganize it as follows:

<h:panelGroup id="myPanel" layout="block">
  <h:graphicImage value="ajaxloading.gif" rendered="#{!myBean.loaded}"/>
  <h:outputText value="#{myBean.myValue}" rendered="#{myBean.loaded}"/>
</h:panelGroup>

<p:remoteCommand autoRun="true" actionListener="#{myBean.initMyValue}" update="myPanel"/>
1
votes

You can use a BlockUI to conditionally block the component while it loads.

  1. Define a preRenderComponent event on the <h:outputText/>

       <h:outputText id="myText">
          <f:event name="preRenderComponent" id="started"/>
       </h:outputText>
    
  2. Define a <p:blockUI/> with the id of the event as the trigger

       <p:blockUI block="myText" trigger="started" />
    

You can customize the blockui to display an image or whatever.

A word of caution: I presume you require this because you're doing some heavy lifting in the getter of that component. Know that the getter will be called several times in the lifecycle of that page. So hiding the fact that the operation is taking a long time will not change the fact. A better design would be to preload and cache the value for that component in a durable scope, rather than the theatrics of a "loading" throbber.

1
votes

This is how I ended up implementing it:

<h:panelGroup id="loginLocation">
<p:graphicImage library="assets" name="small-kit-loader.gif" width="16" height="16" rendered="#{empty mybean.lastLoginLocation}"></p:graphicImage> 
<h:outputText value="#{myBean.lastLoginLocation}" rendered="#{!empty myBean.lastLoginLocation}"/>
</h:panelGroup>
<p:remoteCommand global="false" actionListener="#{actionBean.getUserLoginLocation(myBean.selectedUser)}" name="refreshLoginLocation" id="rc1" autoRun="true" update="loginLocation" process="@this"></p:remoteCommand>

Personally I am not entirely happy with this implementation:

  1. lazy loading state is stored server-side, not client-side where it should be
  2. I have to implement separate method on my backing bean (getUserLoginLocation) to retrieve the value, and explicitly store it in another property (lastLoginLocation). It would have been much cleaner just to have a single getter that is lazy-called after rendering the page in browser
  3. Not easily reusable - depends on backing bean 'loaded' flag (#{empty myBean.lastLoginLocation} in this case), and requires action listener to actually set the value. Any composite component based on this approach would also depend on specific code in backing bean.

Any recommendations on how to improve this code are welcome! :)