Dynamically Disable/Enable of p:tab in p:accordionPanel - jsf

I want only single Tab(not entire accordion panel) to be enabled or disabled dynamically.
I tried following approach, here is my code:
index.xhtml
<p:accordionPanel id="accordionPanelId" widgetVar="accordionPanelWidget">
<p:tab id="tab1" title="First Tab">
<h:outputText value="Contents of Tab1"/>
<h:form>
<p:commandButton value="Enable Tab2" action="#{tabBean.buttonAction}" update=":accordionPanelId:tab2"/>
</h:form>
</p:tab>
<p:tab id="tab2" title="Second Tab" disabled="#{tableBean.disableTab}">
<h:outputText value="Contents of Tab2"/>
</p:tab>
</p:accordionPanel>
TabBean.java
#ManagedBean
#ViewScoped
public class TabBean implements Serializable {
private boolean disableTab=true;
public boolean isDisableTab() {
return disableTab;
}
public void setDisableTab(boolean disableTab) {
this.disableTab = disableTab;
}
public void buttonAction()
{
disableTab = false;
}
}
With the above approach Tab2's content is showing but the Tab Header is still freezes,
May be this is not a god approach to achieve this...
Please suggest any other way or changes in this approach.
EDIT : The above approach is working fine if I update entire Accordion panel as follows:
<p:commandButton value="Enable Tab2" action="#{tabBean.buttonAction}" update=":accordionPanelId"/>
But I don't want to update all the tabs.

You need to bind the disabled attribute of the tab to the disableTab backing bean variable that you'll toggle on the server side. To reflect the changes you made on the server side, you then ajax-update the <p:accordionPanel/> on the client side. Using your current setup, the only changes you'll need to make is:
Bind the disabled attribute to the backing bean variable you've setup
<p:tab id="tab2" title="Second Tab" disabled="#{tabBean.disableTab}">
<h:outputText value="Contents of Tab2"/>
</p:tab>
Automatically switch to the tab after enabling it with the javascript API
<p:tab id="tab1" title="First Tab">
<h:outputText value="Contents of Tab1"/>
<h:form>
<p:commandButton value="Enable Tab2" oncomplete="accordionPanelWidget.select(2)" action="#{tabBean.buttonAction}" update=":accordionPanelId:tab2"/>
</h:form>
</p:tab>

You have to update all the accordionPanel in order to enable/disable tabs. In this case you'll have to update accordionPanelId. However, you can use dynamic="true" attribute in accordionPanel to avoid update the parts that aren't rendered.

Related

How to keep p:tabView tab active after roundtrip to external site

How can i keep active tab after i get redirected from anoter site?
On my interface i would like to return information on a tab window after a user has done credit card authorization using a third party service.
I have frontend done by Primefaces.
Tab view on xhtml:
<p:tabView id="mytabs" styleClass="mystyle" style="min-height: 600px;" activeIndex="#{backinBean.getActiveTab}">
<p:tab title="tab" titleStyleClass="mystyle">
<ui:include src="reports.xhtml"/>
</p:tab>
</p:tabView>
</p:tab>
<p:tab id="anothertab" title="Another tab" titleStyleClass="myclass"
<ui:include src="2ndtab.xhtml"/>
</p:tab>
</p:tabView>
My backinBean:
private Integer activeTab = 0;
public Integer getActiveTab() {
return activeTab;
}
public void setActiveTab(Integer activeTab) {
this.activeTab = activeTab;
}
reports.xhtml contains a button which directs user to credit card authorization service.
After the authorization the user gets redirected back to my site, where information should be displayed on the same tab, which has the button for auth service. Information is displayed correctly, but client doesn't keep the tab active. How to achieve this?
Tested solution from this question didn't work. After redirect page shows the first tab on index.xhtml
index.xhtml tabs
<p:tabView id="mytabs" styleClass="mystyle" style="min-height: 600px;">
<p:ajax event="tabChange" listener="#{backinBean.onTabChange}" />
<p:tab title="tab" titleStyleClass="mystyle">
<ui:include src="firstTab"/>
</p:tab>
</p:tabView>
</p:tab>
<p:tab id="wantedActiveTab" title="Another tab" titleStyleClass="myclass"
<ui:include src="2ndtab.xhtml"/>
</p:tab>
</p:tabView>
backinBean:
private Integer activeTab = 0;
public void onTabChange(TabChangeEvent event) {
TabView tabView = (TabView) event.getComponent();
activeTab = tabView.getChildren().indexOf(event.getTab());
}
Almost there. You should not have removed activeIndex="#{backinBean.activeTab}" from your p:tabView. Also, there is a typo in your EL. You should not use getActiveTab to get activeTab, simply use activeTab.
Also, in this case, the scope of your bean should be set to #SessionScoped.
See also:
How does EL #{bean.id} call managed bean method bean.getId()
How to choose the right bean scope?

p:steps example not functioning

I'm trying use the STEPS component - Primefaces. But in the documentation the tutorial is very poor.
Can someone write an example using steps with property rendered or something like that, how Can I show and hide a panel using STEPS component.
I tried like this but does not work
My xhtml
<p:steps id="testSteps">
<p:menuitem value="Personal" update="testSteps" actionListener="#{BeanTest.shown2()}"/>
<p:menuitem value="Seat Selection" update="testSteps"/>
</p:steps>
<form id="formShowField1" >
<p:panel rendered="#{BeanTest.showfield1}">
<p:outputLabel value="FORM 1"/>
</p:panel>
</form>
<form id="formShowField2">
<p:panel rendered="#{BeanTest.showfield2}">
<p:outputLabel value="FORM 2" />
</p:panel>
</form>
My bean
public void shown1(){
showfield1 = true;
updateEntirePage("formShowField1");
}
public void shown2(){
showfield1 = false;
updateEntirePage("formShowField1");
showfield2 = true;
updateEntirePage("formShowField2");
}
As stated by comments, there are multiple issues with your XHTML code.
1) Use <h:form> instead of <form>
2) The p:steps component is readonly by default. Set readonly="false" in order to have interactive menu items. In this mode, it needs to be placed somwhere inside a h:form to - I get a javax.faces.FacesException: MenuItem must be inside a form element else.
3) Your menuItems update the p:steps component only. Your other panels won't ever show up this way as they are not updated. You should update an element containing them too. Don't know what updateEntirePage is though, especially when invoked twice.
4) Bean names like Java variables typically start with lower case character.
Try it like this:
<h:form>
<p:steps id="testSteps" readonly="false">
<p:menuitem value="Personal" update="#form" actionListener="#{beanTest.shown2()}"/>
<p:menuitem value="Seat Selection" update="#form"/>
</p:steps>
<p:panel rendered="#{neanTest.showfield1}">
<p:outputLabel value="FORM 1"/>
</p:panel>
<p:panel rendered="#{beanTest.showfield2}">
<p:outputLabel value="FORM 2" />
</p:panel>
</h:form>
And in your bean:
public void shown2(){
showfield1 = false;
showfield2 = true;
}

Primefaces On-Demand Loading tabbed panel component does not work

I am trying to do a Primefaces On-Demand Loading tabbed panel component which has a inputTextArea component inside it. I follow this tutorial Primefaces Tutorial.
My .xhtml form looks like this:
<h:form>
<h:outputText
value="Write a comment" />
<p:tabView dynamic="true" cache="true">
<p:tab title="Possitive">
<h:panelGrid columns="2" cellpadding="10">
<p:inputTextarea rows="5" cols="130" />
</h:panelGrid>
</p:tab>
<p:tab title="Negative">
<h:panelGrid columns="2" cellpadding="10">
<p:inputTextarea rows="5" cols="130" />
</h:panelGrid>
</p:tab>
</p:tabView>
<p:commandButton value="Publish"></p:commandButton>
</h:form>
But these inputTextBox are always loaded, nevertheless i did not click on them. Can they be loaded only on demand and how to do this?
Thanks in advance for the help!
Primefaces builds JSF tree for all tabs. Both inputTextarea(s) are created on server side. It can be solved by ui:include. Put the content of p:tab into extra file and use ui:include to fetch the content:
<p:tabView>
<p:tab title="Possitive">
<ui:include src="#{tabViewBean.getTabSource('tabPositive')}"/>
</p:tab>
<p:tab title="Negative">
<ui:include src="#{tabViewBean.getTabSource('tabNegative')}"/>
</p:tab>
</p:tabView>
TabViewBean holds active tab name and is used for resolving tab sources as well. If a tab is NOT ACTIVE then the method tabViewBean.getTabSource() returns another file with EMPTY content:
public String getTabSource(String tabName)
{
return tabName.equals(activeTabName) ?
getTabSourceForTabName(tabName) :
"EmptyTab.xhtml";
}
public String getTabSourceForTabName(String tabName)
{
if ("tabPositive".equals(tabName)) return "TabPositive.xhtml";
if ("tabNegative".equals(tabName)) return "TabNegative.xhtml";
}
You should have EmptyTab.xhtml somewhere. Set listener in p:tabView to listen the tab change event :
<p:tabView listener="#{tabViewBean.tabChangeListener}">
tabChange listener sets the name of active tab:
public void tabChangeListener(UIComponentBase tabView)
{
activeTabName = getTabNameByIndex(tabView.getActiveIndex());
}
public String getTabNameByIndex(int index)
{
if (i==0) return "tabPositive";
if (i==1) return "tabNegative";
}

primefaces accordion panel within single form - validation only on opened tab

I have checked around but I didn't found a clear example on how to submit the accordion using a single form.
The problem I find is when I want to process (validate) only fields of the opened tab.
The structure of my page is as follows:
<h:form>
<p:accordionPanel activeIndex="#{bean.selectedTab}">
<p:ajax event="tabChange" listener="#{bean.tabChangeListener}"/>
<p:tab title="Tab1" id="t1">
<p:inputText id="reqField1" required="true"/>
</p:tab>
<p:tab title="Tab2" id="t2">
<p:inputText id="reqField2" required="true"/>
</p:tab>
</p:accordionPanel>
<p:commandButton update="list" immediate="true" actionListener="#{bean.addRecord}"/>
</h:form>
method:
#SessionScoped
public class Bean{
public void tabChangeListener(AjaxBehaviorEvent evt){
AccordionPanel panel = (AccordionPanel) evt.getComponent();
// getLoadedTabs() returns an empty list
String currentlyLoadedTabId = panel.getLoadedTabs().get(this.selectedTab).getClientId();
}
}
Is there anyway to specify the id of the currently opened tab as part of the process attribute in p:commandButton tag?
UPDATE
When Tab1 is open, on submit, I want to be validated only reqField1 and not reqField2. Viceversa, when Tab2 is open, I want to be validated only reqField2 and not reqField1.
Try this.
Bind the activeIndex of the accordionPanel to a backing bean int attribute
activeIndex="#{myBean.selectedTab}"
Get the id of the currently selected tab using the bound activeIndex. To do this in your actionListener, get a handle on the accordion panel
EDIT: Since your actionListener has been set to immediate, the activeIndex and selectedTab will need to be updated via ajax. Add the following <p:ajax/> to your accordionPanel
<p:ajax event="tabChange" listener="#{bean.tabChangeListener}" />
In your backing bean, you'll now have
public void (AjaxBehaviorEvent evt){
AccordionPanel panel = (AccordionPanel) evt.getComponent();
String currentlyLoadedTabId = panel.getLoadedTabs().get(this.selectedTab).getClientId();
}
Using the the id you got from 2 above, you can include that in the JSF list of things to be rerendered using ajax on the server side. In the same action listener method:
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(currentlyLoadedTabId);
Alternatively, you might just consider placing a command button in each of those tabs and having a form in each tab as well. This way, each command button processes only it's own parent form

The last entry of the datagrid is lost while using partial processing with primefaces

i have a problem about partial processing. (I use primefaces 3.4.1.) I have a dataGrid listing services and each row of the dataGrid has a commandButton, "remove service", to remove the service.
I remove the specified service from my service list in my backing bean and update the datagrid while "remove service" button is clicked.
Also, i have two buttons in the form;
First button, "add service", works to add new service; I add a new object to my service list in my backing bean and update the datagrid while "add service" is clicked.
The second, "save", button works to save all services. When "save button" is clicked, service list is inserted to database.
Btw, i need form validation since there are mandatory fields in my service object. So, the form should be validated while "add service" and "save" are clicked. But, the form
shouldn't be validated while "remove service" button is clicked. If i use 'process="#form"' for "remove service" button, everything is ok. I can remove the specified service
from my service list in the backing bean and the datagrid is updated properly. But as u know, since all form is processed, all input components are validated. If i use 'process="#this"' or 'immediate="true"' for "remove service" button, i can not reach the last entry. I mean, i cannot reach the last service. I guess it hasn't been posted yet when i click the "remove service" button.
So, what should i do? Is there any suggestion about this situation? Thanks in advance...
My code is as below;
<h:form id="formServicesTab">
<h:panelGrid id="pnlSvcTab" columns="1" styleClass="valignTop" width="100%">
<p:dataGrid id="dgServices" var="service"
value="#{subMerchantOperations.subMerchantServices}"
columns="1" width="100%" styleClass="valignTop"
emptyMessage="#{messagebundle.submerc_grdlabel_no_service}" transient="true"
rowIndexVar="index" >
<p:toolbar>
<p:toolbarGroup align="left">
<p:commandButton id="btnRemoveSvc" onclick="loading.show();"
oncomplete="loading.hide();"
value="#{messagebundle.submerc_btn_delete_service}"
actionListener="#{subMerchantOperations.removeService}"
process="#form" update="#form">
<f:setPropertyActionListener target="#{subMerchantOperations.serviceRowIndex}" value="#{index}" />
</p:commandButton>
</p:toolbarGroup>
</p:toolbar>
<h:panelGrid columns="3" styleClass="valignTop" width="100%" bgcolor="F0F0F0">
<p:panel style="background:#F0F0F0;">
<h:panelGrid id="pnlDgSvc" columns="1" width="100%" style="height:100%">
<h:outputText value="#{messagebundle.submerc_label_svc_shortName}" />
<h:panelGrid columns="2">
<p:inputText id="txtSvcName" value="#{service.serviceName}"
required="true" transient="true"
requiredMessage="#{messagebundle.submerc_validation_msg_required}"
converter="UpperCaseConverter" />
<p:message for="txtSvcName" display="text" />
</h:panelGrid>
<h:outputText value="#{messagebundle.submerc_label_svc_website}" />
<p:inputText value="#{service.serviceUrl}" transient="true"/>
</h:panelGrid>
</p:panel>
<p:panel style="background:#F0F0F0;">
<h:panelGrid columns="1" width="100%">
..........
</h:panelGrid>
</p:panel>
<p:panel style="background:#F0F0F0;">
<h:panelGrid id="pgSvcFiles" columns="1" width="100%">
...........
</h:panelGrid>
</p:panel>
</h:panelGrid>
</p:dataGrid>
</h:panelGrid>
<p:toolbar style="background:white">
<p:toolbarGroup align="left">
<p:commandButton id="btnAddNewSvc"
value="#{messagebundle.submerc_btn_addSvc}"
actionListener="#{subMerchantOperations.addNewService}"
process="#form" update="#form" />
<p:commandButton id="btnSaveSubM"
value="#{messagebundle.submerc_btn_sendApproval}"
action="#{subMerchantOperations.saveServices}"
process="#form" update="#form" />
</p:toolbarGroup>
</p:toolbar>
</h:form>
#ManagedBean
#ViewScoped
public class SubMerchantOperations implements Serializable {
private static final long serialVersionUID = 8556103952857187080L;
private List<Service> subMerchantServices = new ArrayList<Service>();
private int serviceRowIndex;
// add new empty service to the service list
public void addNewService() {
try {
Service svc = new Service();
svc.setStartDate(new Date());
getSubMerchantServices().add(svc);
}
catch(Exception ex) {
if (logger.isEnabledFor(Level.ERROR)) {
...
}
}
}
// Remove the specified service using index parameter got from the datagrid
public void removeService() {
try {
getSubMerchantServices().remove(serviceRowIndex);
}
catch(Exception ex) {
...
}
}
// DB Operations
public String saveSubMerchant() {
...
}
public List<Service> getSubMerchantServices() {
return subMerchantServices;
}
public void setSubMerchantServices(List<Service> subMerchantServices) {
this.subMerchantServices = subMerchantServices;
}
public int getServiceRowIndex() {
return serviceRowIndex;
}
public void setServiceRowIndex(int serviceRowIndex) {
this.serviceRowIndex = serviceRowIndex;
}
}
I have no idea what that transient attribute is doing on your <p:dataGrid>. I have reviewed the latest published version of the Primefaces PDF documentation and this is not listed as a valid attribute.
If you are finding that Facelet-based component validation is causing your empty form to fire validation errors on submit of one of your buttons, you might consider creating a method in your backing bean which is called by your submit method, and validates the state of elements on the bean. This is a popular approach with Primefaces, because unlike Seam we are not provided with form-level validation.
However, do check your understanding of immediate=true.
Alternatively you might consider annotating your POJO's properties with JSR303 annotations.
Since you're using Collections, ensure that your Service POJO overrides equals and hashcode to rule out that something screwy isn't going on with your collection and causing your problem.

Resources