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

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.

Related

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

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.

Primefaces Wizard doesn't execute onclick method call

I try to build a form where a user can enter data step by step. After trying a FlowScoped solution I now try to implement the Primefaces wizard.
Description:
My main.xhtml:
<h:form id="antragErfassenForm">
<p:wizard showStepStatus="false" widgetVar="wiz" showNavBar="false" >
<p:tab id="basic_decision_tab" title="Grundsatzentscheidungen">
<ui:include src="/eAkte/basic_decisions.xhtml" />
</p:tab>
<p:tab id="driving_ways_tab" title="Fahrtwegvarianten">
<ui:include src="/eAkte/driving_ways.xhtml" />
</p:tab>
</p:wizard>
</h:form>
Inside the include files I have buttons to navigate between the tabs.
The next button in the first tab 'basic_decisions.xhtml':
<p:commandLink value="Next" onclick="#{eAkteController.callDrivingWays()}; PF('wiz').next();" />
Depending on the result from callDrivingWays() (which evaluates the data of the first tab) the second tab has a different content.
My problem:
The method callDrivingWays() isn't called when clicking the next button on the first tab. Instead it is already called when the first tab is loaded. That's why I always have an empty second page.
I tried switching the method call callDrivingWays() inside an actionListener inside the button or the flowListener of the Wizard. After that the method is called at the correct time, but when clicking on the next button I get an exception, that my component ID has already been used before.
Does anyone has any suggestions?
Try using the flowListener attribute of p:wizard
<p:wizard flowListener="#{yourBB.yourMethod}" nextLabel="Next" backLabel="Back" showNavBar="false" widgetVar="myWizard">
In your backing bean code instead you can implement the actionListener like this:
public String yourMethod(FlowEvent event) {
if(event.getOldStep().equals("driving_ways_tab")){}
if(event.getNewStep().equals("driving_ways_tab")){}
you need getOldStep and getNewStep to perform some changes according to the tab you are entering, exiting.

Primefaces : update dialog content and keep it open

I'm working with JSF and PrimeFaces, and I can't handle the following situation:
I have a dialog, and I placed a dataTable on it. In one of the cells of the table I would like to display given data in 3 different ways, and I'd like to switch between them. So far I managed to switch between these rendering types via commandLink, but my problem is that when I click on one of the 3 links, the dialog closes! Can I update the content of the dialog, and be able to keep it open the same time? (I'm updating which render type to use via myMethod)
my commandLink looks like this:
<p:commandLink id="id" update=":myForm:myDialog" ajax="false"
action="#{myBean.myMethod}" oncomplete="dialog.show()">
If i don't use the ajax=false attribute, the method is not called, and I also tried imediate=true, but that's not it either.
You need to define an p:outputPanel inside your dialog and update the outputpanel, not the dialog itself (that's why your dialog closes):
<p:dialog id="myDialog" ...>
<p:outputPanel id="myOutputPanel">
... your dialog content goes here
</p>
</p:dialog>
and change your commandlink
<p:commandLink id="id" update=":myForm:myDialog:myOutputPanel" ajax="true"
action="#{myBean.myMethod}" oncomplete="dialog.show()">
Regarding the oncomplete="dialog.show()" - I'm not entirely sure if you need that. A precise answer can be given if you provide more code regarding your table and code.
I had the same problem, and solution is to update a form instead of dialog. For example:
<p:dialog id="id_dialog" ...>
<h:form id="id_form">
... content
</h>
</p:dialog>
and commandLink:
<p:commandLink update=":id_form" process="#all" ...>
This worked for me!

PrimeFaces p:accordionPanel inside ui:repeat or c:forEach

I'm trying to create multiple accordionPanels.
I tried to include this tag inside repeat elements (e.g. c:forEach).
The accordionPanel is well rendered, but I'm unable to switch from one tab to another.
These are some codes I tried:
<ui:repeat value="#{myBackingBean.myList}" var="o">
<p:accordionPanel dynamic="true" cache="true" multiple="true" widgetVar="accordion">
<p:tab title="Title 1">
#{o.data_one}
</p:tab>
<p:tab title="Title 2">
#{o.data_two}
</p:tab>
</p:accordionPanel>
</ui:repeat>
And also
<c:forEach items"${myBackingBean.myList}" var="o">
<p:accordionPanel dynamic="true" cache="true" multiple="true" widgetVar="accordion">
<p:tab title="Title 1">
#{o.data_one}
</p:tab>
<p:tab title="Title 2">
#{o.data_two}
</p:tab>
</p:accordionPanel>
</ui:repeat>
Both of these codes produces a list of accordionPanels, each with two tabs.
But I'm unable to switch from one tab to another: If I click one tab it does not open nor close (except for the last accordionPanel).
Noteworthy, if I put two or more accordionPanels by hand (without JSF loops) they works.
Also, I've checked the requests the browser sends when I try to open/close a tab. The requests I intercept are like:
<?xml version="1.0" encoding="utf-8"?><partial-response><changes><update id="mainForm:tabView"><![CDATA[
// something that always use the id of the HTML tag of the _last_ accordionPanel
</update></changes></partial-response>
How can I put an <accordionPanel> inside an <ui:repeat> or <c:forEach> so that I can still switch the accordionPanel's tab?
Its the problem with the widgetVar when you put p:accordion in ui:repeat, it will generate Accordion Panel components with different/dynamic Ids but not with different/dynamic widgetVar when you explicitly specify widgetVar like you're doing in your code.
Which is basically name to Javascript Object to access the Client side Api functions of p:accordion.
So If you are not using that widgetVar of p:accordions anywhere, Remove widgetVar attribute, then the widgetVar will be generated dynamically.
If you still want to use widgetVar, give dynamic name yourself, for example:
widgetVar="accordion_#{o.data_one}"
Note: I tried the above strategy with only on ui:repeat.
Using:Primefaces 3.5 and JSF 2.1.13 and its working for me.

primefaces panelgrid update from datatable

I am getting "Cannot find component with identifier "contentForm:tabView:form:addressDialogPanel" referenced from "contentForm:tabView:form:addressBookTable" " error. How can I update my panelGrid inside the widget?
<h:form id="form">
<p:dataTable id="addressBookTable">
<p:ajax event="rowSelect" listener="#{addressBookController.onRowSelect}"
update="contentForm:tabView:form:addressDialogPanel" oncomplete="addressDialog.show()" />
</p:dataTable>
<p:dialog id="addressDialogId" widgetVar="addressDialog">
<h:panelGrid id="addressDialogPanel" columns="2" cellpadding="4">
</h:panelGrid>
</p:dialog>
</h:form>
The main problem is that you are giving the wrong client ID of component's. Also p:tabView is a component it is not a form. When you define a h:form, it generates a standart HTML form element. And when you submit the page JSF uses POST to submit your data into backing bean. So nesting them is going to occur lot's of issues that you don't expect. You should seperate forms into sections like sideForm or searchForm or etc.
You should detect the correct client ID of your component when you try to update it. You can do this with your browser's developer settings(press f12 for chrome). Then select component with the magnifier button and give that ID into update property. Just like here:
You should read to learn basics of JSF for example from here

Resources