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

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:
p:remote command not working in asynchronous mode
The bug reported here seems to have disappeared
JSF 2.1
Wildfly 8.2.1.Final
Primefaces 8.0.3
Omnifaces 2.7.7
Java 8

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:
A simple gif is displayed.
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.

Related

Primefaces Command button causes page reloading

I am working an primefaces application, I am using
Primefaces 5.x version, Jsf 2.x
which has tabview
in one of the tabs I have command button, by clicking on it pdf export should happen.
For the first time click on button page is getting reloaded, from the second time on words request is going to bean page and downloading is working correctly.
this is my code snippet for command button :
<h:form id="mainForm">
<p:tabView id="myId" activeIndex="#{bean.currentTabIndex}" dynamic="true" cache="false">
<p:tab title="vehicle" id="vehId" styleClass="panelBack">
<h:form id="exportForm">
// some code
<p:commandButton value="export" process="#this" actionListener="# {bean.export()}" ajax="false" id="exportButton" />
</h:form>
</h:form>
If I make dynamic = false, it's working. I mean after clicking export button request is going to bean class, export is working.
What would be the problem? Can somebody help me?
Thank you
Yes your problem is most likely nested that is not allowed in JSF.

PrimeFaces wizard - how can I use onstart/oncomplete for tab change ajax loading

I'm using PrimeFaces 4.
I don't want to use p:ajaxStatus because I only want to show loading for a few components.
So I have a p:blockUI component that I usually show during ajax events by doing this:
onstart="loading.show();" oncomplete="loading.hide();"
But for p:wizard component I don't know how can I do that when the user change tabs (clicking next/back buttons or clicking in the tab title). I tried the following inside the wizard:
<p:wizard id="wizard" flowListener="#{myBean.onFlowProcess}" style="display: inline-block;" backLabel="Prev" nextLabel="Next" showNavBar="true" widgetVar="wizardWV">
<p:ajax event="tabChange" onstart="loading.show();" oncomplete="loading.hide();" />
<p:tab id="tab1" title="My tab 1">
<p:panel>
<h:outputLabel value="test" />
</p:panel>
</p:tab>
<p:tab id="tab2" title="My tab 2">
<p:panel>
<h:outputLabel value="test2" />
</p:panel>
</p:tab>
</p:wizard>
Then I got the error:
<p:ajax> Unable to attach <p:ajax> to non-ClientBehaviorHolder parent
Is there any way I could achieve this?
Thanks
That's is because the wizard isn't a client behavior container.
The standard flow of a wizard is the tabs are just for display purposes in the heading. You should be using the Next/Back triggers for the wizard flow.
Refer to : http://www.primefaces.org/showcase/ui/panel/wizard.xhtml
Has the basic features/flow, but some more links
http://www.primefaces.org/docs/vdl/3.5/primefaces-p/wizard.html
If you're trying to make your tabs clickable then that's a different story, you'll need to setup your flow listener, have them all select the same method/action as the next - but pass a param to the backing bean and manually determine the flow. You could setup a remote call in PF with an onclick attached to access your intent of the waiting on load.
I would look at use case scenarios, maybe Wizard is the wrong component you're looking for?
Looking at the code provided, you're tabs currently are not clickable, and I cannot see your back/next action buttons.

How to show a .gif or message during image loading?

I have a jsf page that loads an image dynamically. The image generation may take a few seconds, so I would like to display a message or a gif so the user knows something is loading (at the moment there is just an empty space during loading).
I have tried to use primeface's deferred loading, but it made no difference at all.
I have a ajaxStatus in my page, but it is not activated during loading.
Here is the code containing the graphicImage:
<p:outputPanel deferred="true">
<h:graphicImage
value="/wfDiagram?processId=#{workflowAction.processId}"/>
</p:outputPanel>
the value is pointing to a servlet that handles image generation.
You should try the primefaces p:blockUI component.
Here is some example:
<p:blockUI widgetVar="loading" trigger="btnFilterProgram" block=":layout">
<h:panelGrid styleClass="custom-painelgrid-center">
<h:outputText value="Loading" />
<p:graphicImage value="resources/img/loading.gif" />
</h:panelGrid>
</p:blockUI>
In this case, a kind of "popup dialog" opens when the user clicks the button with the id btnFilterProgram and the component closes itself after the AJAX request is finished.

Primefaces blockUI to block a DataTable doesn't work when the DataTable is updated via AJAX [duplicate]

I am trying to create a datatable that displays a blockUI whenever it is busy, and I have been mostly successful. It now grays out and shows "Loading..." whenever I click either of two commandButtons, sort the datatable by clicking on a header, or page through the datatable. You can see the code for it below.
The problem is that after I have used one of the commandButtons (which runs an ajax update on the blocked element), subsequent actions do not trigger the blockUI (until I refresh the page). For example:
Load page
Click a datatable header - blockUI appears until table is finished sorting
Click one of the datatable page navigation buttons - blockUI appears until the page is loaded
Click one of the commandButtons - blockUI appears until the button's actionListener has finished
Click a datatable header - table sorts, but blockUI does not appear.
Click one of the datatable page navigation buttons - page loads, but blockUI does not appear
Click one of the commandButtons - actionListener runs and table updates, but blockUI does not appear
Reload the page - everything works properly again
Changing the commandButtons' update="" attribute to ajax="false" causes the sorting/paging to always display the blockUI, but the commandButtons to never display the blockUI.
Any ideas?
<div class="buttonDiv">
<p:commandButton ... update="resultsPanel" id="submitButton" ... />
...
<p:commandButton ... update="resultsPanel" id="resetScenarioButton" ... />
</div>
<p:panel header="Results Grid" id="resultsPanel">
...
<p:dataTable ... id="VAResults" ... >
...
</p:dataTable>
....
</p:panel>
<p:blockUI block="resultsPanel" trigger="submitButton, resetScenarioButton, VAResults">
Loading...
</p:blockUI>
The trigger attribute binds jQuery listeners on the specified elements. However if you update an element the binding gets lost. I don't know if it works, but you could try moving the <p:blockUI inside the resultsPanel. I suspect that when you update the panel the blockUI gets updated too and thus re-binding the listener to the data table.
<p:panel header="Results Grid" id="resultsPanel">
...
<p:dataTable ... id="VAResults" ... >
...
</p:dataTable>
....
<p:blockUI block="resultsPanel" trigger="submitButton, resetScenarioButton, VAResults">
Loading...
</p:blockUI>
</p:panel>
I've had the same problem and kind of simillar scenario:
<p:dataTable>
....
<p:ajax event="rowSelect" update="buttons" global="false" onstart="blockMessageButtons.show();" oncomplete="blockMessageButtons.hide();"/>
</p:dataTable>
<p:outputPanel layout="block" id="buttons">
<!-- content to be blocked -->
</p:outputPanel>
<p:blockUI block="buttons" widgetVar="blockMessageButtons"/>
The problem was that panel buttons was both updated by ajax, and blocked by blockUI. I had to divide it in two:
<p:dataTable>
....
<p:ajax event="rowSelect" update="buttons-content" global="false" onstart="blockMessageButtons.show();" oncomplete="blockMessageButtons.hide();"/>
</p:dataTable>
<p:outputPanel layout="block" id="buttons-container">
<p:outputPanel layout="block" id="buttons-content">
<!-- content to be blocked -->
</p:outputPanel>
</p:outputPanel>
<p:blockUI block="buttons-container" widgetVar="blockMessageButtons"/>
#siebz0r already provided the explanation why blockUI stops working when a component is updated.
I am using a one blockUI element in the template for all other pages and thus don't want to include more blockUI elements.
If the blockUI element is updated as well one does not need to move the blockUI inside the component that will be updated.
Here is an example:
<p:panel id="surroundingPanel">
...
<p:commandButton value="ButtonName" styleClass="blockUi"
action="actionToBeExecuted" update=":surroundingPanel :blockUiBinding" />
</p:panel>
<p:outputPanel id="blockUiBinding">
<p:blockUI block=":elementToBeBlocked" trigger="#(.blockUi)">
Loading ...
</p:blockUI>
</p:outputPanel>
The element blockUiBinding can be located anywhere, as long as it can be updated. It is wrapping the blockUI element, because blockUI generates at least two different divs. So when the wrapping element is updated also the blockUI will be updated.

Primefaces commandButton action attribute not being called

I am creating a JSF application with primefaces. Basically I have this view. There is a tab view which for each tab contains a command button and a accordion panel (another kind of tab view). The tabs of the accordion panel among other elements, contain each one command button. My problem is that the first command button (under 1 level of tabs) calls correctly the action method when it is clicked, while the second button (under 2 level of tabs) does not. I should say that both the tabView and accordionPanel are working correctly, since they are displaying the information they should display.
I am posting a simplified version of my view so that you can see what is happening.
<h:form>
<p:tabView id="unitTabs" orientation="left" dynamic="true" cache="false" var="unit" value="#{unitController.getUnitsOfLoggedInUser(loginController.checkedUser)}">
<p:tab id="unitTab" title="#{unit.unitName}">
<p:commandButton value="Add Lecture" action="#{unitController.setTemporary(unit)}" onclick="createLectureDialog.show()">
<p:accordionPanel id="lectureTabs" value ="#{lectureController.getLecturesForUnit(unit)}" var="lecture" dynamic="true" cache="false">
<p:tab title="#{lecture.lectureName}">
<p:commandButton value="Add Criterion" action ="#{lectureController.setTemporary(lecture)}" onclick="createCriterionDialog.show()" >
</p:tab>
</p:accordionPanel>
</p:tab>
</p:tabView>
</h:form>
What am i doing wrong? Thanks
This construct requires that the EL expression in the value attribute of the <p:tabView> and <p:accordionPanel> returns exactly the same value during processing the form submit as it was during the initial display.
If the bean is request scoped and/or the value depends behind the scenes on a request based parameter or variable and it thus returns a different value on every request, then it will fail because JSF cannot locate the button which was been invoked. To fix it, the bean needs to be placed in the view scope and the getter method should not contain any business logic, or any request based parameters or variables should be retained in the subsequent requests.
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 4.

Resources