Switch to tab in tabView with invalid input - jsf

The problem is really simple. I have a tabview with multiple tabs:
<h:form>
<p:tabView id="tabView">
<p:tab>
<p:inputText required="true"></p:inputText>
</p:tab>
<p:tab>
<p:inputText required="true"></p:inputText>
</p:tab>
</p:tabView>
<p:commandButton value="submit" action="#{myBB.submit}" update="tabView"/>
</h:form>
What I would like is to switch to the tab with missing field when the client-side validation fails. I thought that this must be a common use-case for form in tabs, but I cannot find any solution.
The only thing I can think of is call some oncomplete action on the submit button which would go through the DOM and find the invalid components, but it seems overly complicated for a thing which I believe should be default behavior.

So I've found a way to do that. After the ajax completes, I created an oncomplete method to search through the tabs and select the correct one.
First I had to modify the source a bit(just added ids and widgetVar):
<h:form>
<p:tabView id="tabView" widgetVar="tabViewWv">
<p:tab id="tab1">
<p:inputText required="true"></p:inputText>
</p:tab>
<p:tab id="tab2">
<p:inputText required="true"></p:inputText>
</p:tab>
</p:tabView>
<p:commandButton ... oncomplete="switchToInvalidTab()"/>
</h:form>
and the javascript:
function switchToInvalidTab() {
var lastIndex = 0;
var tabParent = '#mainForm\\:tabView\\:';
var tabs = ['tab1', 'tab2']; //can add more, but the order must be the same as in form
tabs.some(function (tabName){
if ($(tabParent + tabName)) { //tab may not exist in my app
if ($(tabParent + tabName).find(':input').hasClass('ui-state-error')){
PF('tabViewWv').select(lastIndex);
return true;
}
lastIndex++;
}
return false;
});
}
There are more tabs in my app and some of them doesn exist sometimes and this code works for all cases. Maybe it helps someone

Related

Primefaces tabview events

I'm migrating app from Primefaces 3.5 to last version, actually 6.2. I have a problem with events on close and on change tabs. I have dynamically created tabs.
On TabChangeEvent tabClientId is different than in UIComponent, but it still works and I can change correctly between tabs. Real problem is when I close tab, then in contextEvent I have tabClientId = [object Object] and tabIndex is null so app explodes here:
int tabindex = Integer.parseInt(params.get(clientId + "_tabindex"));
Someone knows how to fix this?
<p:tabView var="tabview" value="#{tabBean.tabs}"
binding="#{tabBean.tabGlTabView}" id="panelGl"
widgetVar="panelGlJs" dynamic="true" styleClass="contentPanel"
activeIndex="#{tabBean.index}">
<p:tab title="#{tab.title}"
closable="true">
<ui:include src="#{tabBean.tab}" />
</p:tab>
<p:ajax event="tabChange"
onstart="setActive()"
update="#(this)" />
<p:ajax event="tabClose"
listener="#{tabBean.onTabClose}" />
</p:tabView>

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";
}

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

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.

JSF - p:tabView - changing tab and previous view still displayed

Inside one tab, I can cilck on a button to display an "h:groupPanel". If I do this, and then change the active tab, the groupPanel that has just appeared is still visible, and appears on top of the now current tab.
The code for the tabView is:
<p:tabView dynamic="true" cache="false" id="characterTabView">
<p:tab title="View" id="viewTab">
<ui:include src="/sections/character/view.xhtml" />
</p:tab>
<p:tab title="Upgrade" id="upgradeTab">
<ui:include src="/sections/character/upgrade.xhtml" />
</p:tab>
</p:tabView>
The "upgrade" page looks like
<h:panelGroup id="searchCompetencies" rendered="#{characterBean.rightPanel == 'searchResult'}">
<h:panelGroup styleClass="characterUpgradeLeft">
<ui:include src="/sections/character/searchViewLeft.xhtml" />
</h:panelGroup>
<h:panelGroup id="competencyCreation" rendered="#{characterBean.rightPanel == 'competencyCreation'}">
<!-- The panel that is made visible -->
</h:panelGroup>
And the page in which the component is made visible (searchViewLeft):
<p:fieldset legend="Create competency" styleClass="createCompetencyStart">
<h:form>
<p:commandButton value="Create competency" type="button" onclick="toggleCreate()" update="upgradeTab" />
</h:form>
</p:fieldset>
where "toggleCreate" changes the value of "characterBean.rightPanel".
Would you have any idea as to what I am missing here?
Thanks a lot :)

JSF - p:dataTable - p:commandButton doesn't work even inside p:column

I have a commandButton inside a DataTable. However the "action" isn't called when I click on it (same effect with an actionListener); I added logs at the beginning of the server action, and it never shows.
Please note that the button works fine when outside from the datalist.
Here is my code:
<h:form id="compSearchForm">
<p:dataTable var="competency" value="#{competencySearchBean.matchingCompetencies.toArray()}">
<p:column>
<f:facet name="header">
<h:outputLabel value="Title" />
</f:facet>
<h:outputText value="#{competency.title}" />
</p:column>
<p:column>
<p:commandButton value="Go" id="actionButton" action="#{myBean.doAction}" />
</p:column>
</p:dataTable>
</h:form>
Would you have any idea as to what the issue might be?
I added your button column code to an existing of mine along with a dummy method in by backing bean. The method is called correctly when I press the button it works correctly. Here is the code I added to my dataTable:
<p:column>
<p:commandButton value="Go" id="actionButton" action="#{tableBean.buttonAction()}" />
</p:column>
Here is the buttonAction method in my backing bean:
public void buttonAction()
{
int a = 0;
for(int i = 0; i < 100; i++)
a = i;
}
I put a breakpoint on this method, so I know it is getting called when I click the button.
I went a step further and used your datatable code (with my data source) and the method is still being called when I press the button. I don't know what development environment you are using, but if it has a debug mode put a breakpoint on your doAction method, and a few other strategic locations in your bean, then run your project in debug mode to see what is going on.
I think you have to change to:
<h:form id="compSearchForm">
<p:dataTable var="competency" value="#{competencySearchBean.matchingCompetencies.toArray()}">
<p:column>
<f:facet name="header">
<h:outputLabel value="Title" />
</f:facet>
<h:outputText value="#{competency.title}" />
</p:column>
</p:dataTable>
<p:commandButton value="Go" id="actionButton" action="#{myBean.doAction}" />
</h:form>
I don't think you can have the button inside the dataTable. Try this.
see this example - http://www.primefaces.org/showcase/ui/datatableRowSelectionByColumn.jsf
I think you must prefix the id with the form ID
see how it updates ":form:display" instead of only "display"
worked for me
I had the same problem and after trying different ways, I resolved it by initializing my List (the list that I use in the p:datatable) in the init method of the Bean.
With this form, the list goes to the page with some values.
Even when I put List<Paciente> myList = new ArrayList<Paciente>(); it didn't work.
I have to put some values in the list. That was the only way that resolved the problem.

Resources