JSF Component IDs ignored when dynamically generating Views - java

I ran into a bit of a problem.
I generate Views dynamically by iterating a list of strings via the c:forEach tag
and then including them via the facelets include tag.
This works fine for building the layout but shows some strange behaviour.
I have a primefaces tabview containing 2 tabs.
For the first Tab (the one that gets initially displayed) the component id is set (eg. tabview:categoryTab) but that's not the case for e.g. the second tab, here I only get tabview: for the component id (but it actually should be tabview:usrTab)
Why does JSF override the id I set for the second Tab? Am I missing some crucial information from the spec?
I use JSF 2 with Primefaces 3.6 (snapshot build)
(And yes, I use the snapshot build on purpose and have tested this with stable PF releases as well but the same behaviour occurs)
edit
Code:
admin.xhtml
<ui:composition template="/templates/commonLayout.xhtml">
<ui:define name="content">
<p:panel id="parentPanel">
<h:outputText value="Verwaltung" />
<br />
<p:tabView id="tabview">
<!-- insert marker -->
<c:forEach items="#{adminTabs}" var="tab">
<ui:include src="#{tab}" />
</c:forEach>
</p:tabView>
</p:panel>
</ui:define>
</ui:composition>
catTab.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<p:tab title="Categories Tab" id="catTab">
....
</p:tab>
</ui:composition>
usrTab.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<p:tab title="Users Tab" id="usrTab">
....
</p:tab>
</ui:composition>
testTab.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<p:tab title="TestTab" id="testTab">
....
</p:tab>
</ui:composition>
ContentProvider.java
public class ContentProvider {
....
#Produces
#Named("adminTabs")
public List<String> getTabs(){
List<String> components = new ArrayList<String>();
components.add("/templates/tabs/catTab.xhtml");
components.add("/templates/tabs/usrTab.xhtml");
components.add("/templates/tabs/testTab.xhtml");
return components;
}
....
}
This gets generated:
<ul class="ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" role="tablist">
<li class="ui-state-default ui-tabs-selected ui-state-active ui-corner-top" aria-expanded="true" role="tab">
Categories
</li>
<li class="ui-state-default ui-corner-top" aria-expanded="false" role="tab">
TestTab
</li>
<li class="ui-state-default ui-corner-top" aria-expanded="false" role="tab">
Benutzer und Rollen
</li>
</ul>
So, to reiterate: Only the id for the first tab is retained, the ids for the other tabs are generated despite being set in the xhtml code...

I ran into something very similar with Richfaces 4 and JSF 2. I managed to hack around it by including an EL expression in the id attribute. For me
<h:form id="staticName">
was being rendered into
<form id="j_idblah>
but once I did
<h:form id="#{_objectInContext}">
it began rendering properly. Pretty hacky, but for now it'll work. Good luck!!

I ran into the same problem when including facelets with a c:forEach loop.
What worked for me was specifying the Ids as EL-Constants: id="#{'address_street'}"

Related

Conditional navigation not working properly in JSF

I am developing a website with JSF 2.0, hibernate and primefaces.
I'm implementing an admin panel where the website admin can put the whole application on "maintenance mode". This is done by changing a flag on an application scoped bean (called on purpose AppConfigurationBean).
If the flag is active the user is necessarily returned to a page, called maintenance.xhtml, by a conditional navigation rule declared in faces-config.xml.
The user can bypass the maintenance page by inserting a special bypass key which can be given seldom by the admin. If he does so, a flag is activated on a special session scoped bean, which holds information about the user and is called CurrentClientSessionBean.
The navigation rule works if the value returned from a special method on the CurrentClientSessionBean, which take in counts the maintenance mode flag on the application bean and the "has bypass key" flag on himself.
Here is the navigation rule on faces-config.xml:
<navigation-rule>
<description>System</description>
<from-view-id>*</from-view-id>
<navigation-case>
<display-name>Maintenance Mode</display-name>
<from-outcome>*</from-outcome>
<if>#{currentClientSessionBean.toReturnToMaintenance}</if>
<to-view-id>/maintenance.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
Here is the maintenance.xhtml page:
<ui:composition id="maintenance" xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:plm="http://java.sun.com/jsf/composite/plmCustomComponents">
<f:verbatim><!DOCTYPE html></f:verbatim>
<html>
<h:head>
<title>#{msg.applicationTitle}</title>
<meta charset="UTF-8"></meta>
<meta http-equiv="content-type" content="text/html; charset=utf-8"></meta>
<h:outputStylesheet library="default" name="css/normalize.css" />
<h:outputStylesheet library="default" name="css/icons.css" />
<h:outputStylesheet library="default" name="css/main.css" />
</h:head>
<f:metadata>
<ui:include src="metadata/browserViewParams.xhtml" />
</f:metadata>
<h:body>
<div id="legalPart">
<ui:insert name="legalPart">
<ui:include src="/structure/legalPart.xhtml" />
</ui:insert>
</div>
<p:panel>
<h1>
<h:outputText value="#{msg.maintenancePageTitle}" />
</h1>
<p>
<h:outputText value="#{msg.maintenancePageBodyMsg}" />
</p>
<h:form>
<p:outputLabel for="bypassKeyInput"
value="#{msg.maintenancePageInsertBypassKeyLabel}" />
<br />
<p:inputText id="bypassKeyInput"
value="#{maintenanceBacking.keyBuffer}" required="true" />
<br />
<p:message for="bypassKeyInput" />
<p:commandButton value="#{msg.checkPassword}"
action="#{maintenanceBacking.checkBypassKey}" ajax="false" />
</h:form>
</p:panel>
<ui:debug />
</h:body>
</html>
</ui:composition>
The maintenance backing is really simple, it simply checks if the bypasskey matches the true one and does a simple navigation to the home page if true.
Now the CurrentClientSessionBean method for the conditional navigation is this one:
/**
* Special getter that determines if user should return to maintenance
*
* #return
*/
public boolean isToReturnToMaintenance(){
try{
AppConfigurationBean appConfigurationBean = JSFAppUtils.findApplicationBean("appConfigurationBean");
boolean result = ((!this.hasBypassKey) && appConfigurationBean.isMaintenanceModeOn());
return result;
}catch(NotFoundException e){
logger.error("NotFoundException thrown in isToReturnToMaintenance method", e);
throw new RuntimeException("NotFoundException thrown in isToReturnToMaintenance method", e);
}
}
Here is the main problem: for some odd reason, when the maintenance mode is active the user is forced to return to maintenance page even if he puts the right bypass key in the input.
Furthermore: There must be something really wrong within my application, because i tried to comment the conditional navigation rule, which means that maintenance page should be unreachable and changes to the maintenance mode should have no effect on the application, but still the maintenance mode system went on working with the same attitude I described.
It seems that I added a redirect on the homepage which was connected to the maintenanceModeOn boolean flag, and didn't take in count the hasBypassKey flag.
The rule was deeply nested in some part of the code which I wrote long long time ago and I forgot about.
<h:panelGroup rendered="#{appConfigurationBean.maintenanceModeOn}">
<script>
window.location = "maintenance.faces";
</script>
</h:panelGroup>`

JSF2 commandButton; AJAX or non AJAX, it refreshed the whole page

this is a simple example which demonstrates the case.
you have a form with a Panel and two commandButton, one is AJAX the other is not. by clicking on any of them, an InputText will be created in the backing bean and added to the Panel.
My managed bean:
#ManagedBean
public class DynamicPanel {
private Panel dynmaic;
public Panel getDynmaic() {
return dynmaic;
}
public void setDynmaic(Panel dynmaic) {
this.dynmaic = dynmaic;
}
public String adddynamic(){
InputText text = new InputText();
dynmaic.getChildren().add(text);
text.setValue(text.getId()+" Size= "+ dynmaic.getChildren().size());
return null;
}
public String removeall(){
this.dynmaic.getChildren().clear();
return null;
}
}
My XHTML page
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui" xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<p:panel id="dynamic" binding="#{dynamicPanel.dynmaic}">
</p:panel>
<h:commandButton value="Add with AJAX" id="ajaxBtn" >
<f:ajax onevent="onClick" execute="#{dynamicPanel.adddynamic()}" render="dynamic" />
</h:commandButton>
<h:commandButton value="Add" action="#{dynamicPanel.adddynamic}" />
<h:commandButton value="remove all" action="#{dynamicPanel.removeall}" />
</h:form>
</h:body>
</html>
my faces-config.xml is empty.
Now, I have three issues with the code above. Could someone please clarify it to me, I'm new to JSF2.
the first is, why both command buttons behave the same? clicking on ether one would refresh the whole page.
the second issue is, why clicking on the non AJAX commandButton adds two Inputfieds at a time?
the third is, why changing the scope of the managed bean to #SessionScoped will give an error once you load the page? ( somehow just loading the page, the form issues an ajax request without me clicking on the commandButton. Why is that?
Try the following, that should work better.
<h:commandButton value="Add with AJAX" id="ajaxBtn" >
<f:ajax onevent="click" execute="ajaxBtn"
render="dynamic"
listener="#{dynamicPanel.adddynamic()}"
</h:commandButton>
As far as I know, for f:ajax execute attribute the id of the components should be given, and you should call methods such as addDynamic() in listener attribute.
When you click on the non-ajax button, it posts the whole form which also includes your ajax command. so basically your addDynamic() function is called twice, one through ajax command and the second through non-ajax command..

jboss-seam-pdf inside rich:modalPanel cannot work

I have a modalPanel declared as
<rich:modalPanel id="printEmailExcel" minWidth="800" minHeight="600" resizable="true">
<div style="width: 1055px; height: 700px; overflow: auto">
<s:decorate template="/view/popup/printEmailExcelPopup.xhtml"styleClass="popupCDiv">
<ui:param name="panelid" value="printEmailExcel" />value="/view/printForm/order.xhtml" />
</s:decorate>
</div>
</rich:modalPanel>
There is also a commandbutton to show the modalPanel in the same page
<s:button oncomplete="#{rich:component('printEmailExcel')}.show()" image="/images/icons/btn_print.gif" ></s:button>
inside the printEmailExelPopup.xhtml, there is a
<h:form><s:button view="/view/print/order.xthml" /></h:form>
order.xhtml :
<p:document xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:p="http://jboss.com/products/seam/pdf">
The document goes here.
< /p:document>
The problem is that when i click the s:button in the modalPanel, it only refresh the whole page and the pdf file IS NOT downloaded. However, if i place the s:button outside the modalPanel(same level of the rich:modalPanel, it works normally). Any Help?
try surrounding your commandButton with form tags.

Partial rendering JSF components

dwelling over how to partial render (divs), by including different source files (with panels and components). Depending on menu actions. If understood the JSF phases correctly, the View is rebuilt during the Render Response, the last phase. And if I have events and actions, they will be invoked during the Invoke Application phase, the phase before.
All I want to do is to set the including xhtml page for a specific menu command via ajax, before the View is re-rendered. But the ui:include always get invoked before the menu action. I've tried with richfaces 4 (a4j:param, rich:panel, etc) and standard JSF 2.0 (f:param, h:panelGroup) components, but the the ui:include always get invoked before the action.
What should I do to process the menu action (to set the including page), before the ui:include gets invoked?
PS. This must be the standard patter, instead of including static content. But I find very few examples on this on the net ?!
Menu.xhtml
<rich:toolbar itemSeparator="line">
...
<rich:dropDownMenu mode="ajax">
<f:facet name="label">
<h:panelGroup>
<h:outputText value="Menu 1" />
</h:panelGroup>
</f:facet>
<rich:menuItem id="newActivityMenu" action="#{navigationBean.menuAction}" render="content" label="New">
<a4j:param id="newActivityParam" name="includeContentPage" value="/user/Create.xhtml" />
</rich:menuItem>
...
NavigationBean.Java
#ManagedBean
#RequestScoped
public class NavigationBean {
public String menuAction() {
String param = JsfUtil.getRequestParameter("includeContentPage");
this.includedContentPage = param;
JsfUtil.log(this, "Including Content Page : " +param);
FacesContext.getCurrentInstance().renderResponse();
return "";
}
public String getIncludedContentPage() {
if(includedContentPage == null)
return "";
else if(!includedContentPage.endsWith(".xhtml"))
includedContentPage += ".xhtml";
JsfUtil.log(this, "Get Content Page : " +includedContentPage);
return includedContentPage;
}
layoutClient.xhtml
...
<ui:define name="top">
<ui:include src="/resources/viewComponents/menuTop.xhtml"/>
</ui:define>
<ui:define name="content">
<ui:include src="#{navigationBean.includedContentPage}"/>
</ui:define>
...
masterLayout.xhtml (added)
...
<h:body>
<div id="top" >
<ui:insert name="top">Top Default</ui:insert>
</div>
<div id="left">
<ui:insert name="left">Left Default</ui:insert>
</div>
<ui:insert name="content">Content Default</ui:insert>
</h:body>
..
You must have a template page as well since you are defining top and content in layoutClient.xhtml so I think you are trying to be too general with the layoutClient.xhtml page as it appears to be functioning as a template as well. Lets assume your template page is called template.xhtml. The standard pattern you eluded to is to make your template page something like this:
template.xhtml
...
<ui:insert name="top">
<ui:include src="/resources/viewComponents/menuTop.xhtml"/>
</ui:insert>
...
<ui:insert name="content" />
...
This means that all your pages contain the menu at the 'top' (by default, they can override this) and that they must specify their own content, which makes sense.
Now, instead of trying to make a page like layoutClient.xhtml that does tricky stuff to determine which content is inserted, create each page seperately like this:
page1.xhtml
<ui:composition template="template.xhtml">
...
<ui:define name="content">
<p>This is a page that defines some content and also includes my menu that it inherits from template.xhtml</p>
</ui:define>
...
</ui:composition>
page2.xhtml
<ui:composition template="template.xhtml">
...
<ui:define name="content">
<p>This is another page that defines some content and also includes my menu that it inherits from template.xhtml</p>
</ui:define>
...
</ui:composition>
Both of these pages inherit your menu and put the content in the appropriate place.
With that kind of configuration, all your menuAction() method needs to do is return a link to page1.xhtml or page2.xhtml. Also, you don't need any complex use of parameters or manual calls to renderResponse() or a4j:param tags!

JSF JSTL problem with nested forEach

Inside a nested foreach, accessing the same variable is returning different values. This happens when the page is reloaded, not on first load.
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
(...)
xmlns:c="http://java.sun.com/jstl/core"
xmlns:h="http://java.sun.com/jsf/html">
(...)
<c:forEach items="#{controller.availableTransitions}" var="transition">
<c:forEach items="#{transition.availableTransitions}" var="transitionItem">
<h:outputText value="1_#{transitionItem.name} 2_#{transitionItem.name}" />
3_#{transitionItem.name} 4_#{transitionItem.name}
</c:forEach>
</c:forEach>
</ui:composition>
After page reload, transitionItem.Name returns the correct value for 3 and 4, and different values for 1 and 2. Maybe a JSF-JSTL integration problem?
I see that you are using Facelets.
Maybe you can try to replace your <c:forEach> by <ui:repeat>...
The code will then become:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
(...)
xmlns:c="http://java.sun.com/jstl/core"
xmlns:h="http://java.sun.com/jsf/html">
(...)
<ui:repeat value="#{controller.availableTransitions}" var="transition">
<ui:repeat value="#{transition.availableTransitions}" var="transitionItem">
<h:outputText value="1_#{transitionItem.name} 2_#{transitionItem.name}" />
3_#{transitionItem.name} 4_#{transitionItem.name}
</ui:repeat>
</ui:repeat>
</ui:composition>
In general, I try to use ui:repeat most of the time. When I was having c:set issues, I found this blog, which was very helpful and may apply in your case also.
https://rogerkeays.com/jsf-c-foreach-vs-ui-repeat
Found a workaround, by getting rid of the inner forEach loop, thus returning a linear list from the controller.

Categories