defer loading of an output panel within a tab managed by primefaces tabview tag

660 Views Asked by At

Presentation

I am facing an issue to get an output panel load in deferred mode when the panel is included in a tab managed by Primefaces.

Here is a code sample:

<p:tabview value="#{b.tabs}" var="tab" dynamic="true" cache="false">
  
   <p:tab title="#{tab.name}">
        <p:outputpanel id="reportsDisplayId" >
            <p:outputPanel id="reportsPanel" deferred="true" >
                <h:outputText id="reports" escape="false" value="#{tab.report}" />           
            </p:outputPanel>
        </p:outputpanel>
   </p:tab>
</p:tabView>

"tab.report" is generated by Jasper. Generating this report takes time in most cases. This is the reason for using the deferred mode.

Tabs are managed using p:tabView in dynamic mode and no caching, so that tabs are refetched from the server at each tab change.

This works fine for the first tab: the display of the panel is effectively deferred without preventing the context from being displayed. Upon switching between tabs, the whole content of the tab is displayed only when the report is ready.

I have tried using deferredMode="visible", but this feature seems to intended for scroll visibility. I have tried enclosing the h:form into a p:outputPanel and updating the panel instead of the form.

Without success so far.

Working with p:RemoteCommand

I have progressed using p:RemoteCommand and found that others have been following that path, however with some difficulties. I will post my progress.

Here are related posts:

JSF 2.1 Wildfly 8.2.1.Final Primefaces 8.0.3 Omnifaces 2.7.7 Java 8

1

There are 1 best solutions below

0
LaurentV On

Here is a solution which relies on p:remoteCommand and not on the deferred attribute of output panel.

<h:form id="boardForm">

  <p:tabview id="boardTabs" value="#{b.tabs}" var="tab" dynamic="true" cache="false">

     <p:ajax event="tabChange" listener="#{board.onTabChange}"/>

     <p:tab title="#{tab.name}">

       <!-- First rendering: display a loading gif -->
       <p:graphicImage value="/resources/img/loading.gif" 
                       rendered="#{!board.loading}" alt="loading" />

       <!-- Second rendering: display the report itself -->
       <p:outputPanel id="reportsPanel"  rendered="#{board.loading}" >
           <h:outputText id="reports" escape="false" value="#{board.getJasper(tab)}"/>
       </p:outputPanel>

       <p:remoteCommand name="deferredLoader_#{board.tabIdx}" async="true" autoRun="true"
                       actionListener="#{board.setLoading(true)}" rendered="#{!board.loading}"
                       update=":boardForm:boardTabs:#{board.tabIdx}:jspReportDisplay" />

    </p:tab>

  </p:tabView>

</h:form>

The rendering goes in two phases:

  1. A simple gif is displayed.
  2. The whole report is displayed. However, the gif is displayed as long as the report is not ready.

Detailed comments:

  • Upon tab switch, the event "tabChange" invokes the bean method "onTabChange", which sets "loading" to false and records the active tab index.
  • As "loading" is false, the gif is displayed, the report is not displayed, and the remote command is run.
  • The remote command is async so that it is possible to switch to another tab before the active tab is fully displayed.
  • The remote command sets back "loading" to false before updating the tab for the second phase, so that: (1) the gif is not displayed again; (2) the report is now generated; (3) the auto-run remoteCommand is not run infinitely many times.
  • the name of remoteCommand is indexed to the active tab to ensure unicity of names for each remoteCommand in various tabs.

Well, I guess there are more elegant solutions than this one. This is the one I found.