In JSF2, how to know if composite component has children? - jsf

I'm writing a composite component, you have a special tag named:
<composite:insertChildren />
Which inserts all the component's children there. Is there any way to know whether the component has children? Like a boolean value that could go on a "rendered" attribute.

The basic expression you're after is the following:
#{cc.childCount} or more elaborately:
#{component.getCompositeComponentParent(component).childCount}
E.g. the following composite component:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cc="http://java.sun.com/jsf/composite"
>
<cc:interface/>
<cc:implementation>
<h:outputText value="Children: #{cc.childCount}" />
</cc:implementation>
</html>
used on the following Facelet:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:test="http://java.sun.com/jsf/composite/test"
>
<h:body>
<test:myCom>
<h:outputText value="first child" />
<h:outputText value="second child" />
</test:myCom>
</h:body>
</html>
will print Children: 2.
Thus #{cc.childCount != 0} will tell you whether a composite component has children or not.

I've encountered the same problem and managed to find children of a composite component within it's facet 'javax.faces.component.COMPOSITE_FACET_NAME'.
In Java it's like this:
// we are within some method of UIComponent
UIComponent childrenHolderFacet = getFacets().get("javax.faces.component.COMPOSITE_FACET_NAME");
Iterator<UIComponent> childrenIt = childrenHolderFacet.getChildren().iterator();
...
In JSF it's something like:
#{component.getFacets().get("javax.faces.component.COMPOSITE_FACET_NAME").children}
Hope it helps.

At least on Mojarra this does not work. A composite component's children get inserted just fine but accessing #{cc.parent} or #{cc.children} does not work on 2.0.2, and #{cc.childCount} always returns 0 on 2.0.4, regardless of the number of children.

Related

How JSTL tags evaluated in JSF 2.1 for the below

I have the below Test.xhtml where i can select mode as Sea/Air. I don't want to load the all the 4 pages(Page1.xhtml, Page2.xhtml, Page3.xhtml, Page4.xhtml) into the jsf view tree. In my scenario, mode once selected and saved that can not be changed to other mode. The saved mode will be shown as view mode. Since at any point of time i need only 2 files (Page1.xhtml, Page2.xhtml (or) Page3.xhtml, Page4.xhtml)... I am using the JSTL tag handler for dynamic including the pages. the below thing is working fine. But i am not able to understand how the rendering working.
Test.xhtml
<!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:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head><title>JSF And JSTL</title>
<link href="../css/styles.css"
rel="stylesheet" type="text/css"/>
</h:head>
<h:body>
<div align="center">
<h:form id="form1">
<h:panelGrid columns="2">
<h:outputLabel value="Select Mode"/>
<h:selectOneMenu id="selectPageId" value="#{testBean.selectedMode}">
<f:selectItem itemLabel="Select" />
<f:selectItem itemLabel="Sea" itemValue="sea" />
<f:selectItem itemLabel="Air" itemValue="air" />
<f:ajax execute="#this" render="pageGroup"/>
</h:selectOneMenu>
</h:panelGrid>
<h:panelGroup id="pageGroup">
<c:if test="#{'sea' eq testBean.selectedMode}">
<ui:include src="Page1.xhtml" />
<ui:include src="Page2.xhtml" />
</c:if>
<c:if test="#{'air' eq testBean.selectedMode}">
<ui:include src="Page3.xhtml" />
<ui:include src="Page4.xhtml" />
</c:if>
</h:panelGroup>
</h:form>
</div>
</h:body>
</html>
TestBean.java
package com.test;
#ManagedBean(name = "testBean")
#ViewScoped
public class TestBean {
private String selectedMode;
public String getSelectedMode() {
return selectedMode;
}
public void setSelectedMode(String selectedMode) {
this.selectedMode = selectedMode;
}
}
Updated Again
Please help to clarify the below.
When i send a request first time to access the Test.xhtml, the jsf view (UIViewRoot) gets created. Since testBean.selectedMode by default is null.. this created view would not contain any component details of <c:if> and <ui:include> which are inside of <h:panelGroup id="pageGroup">.
After that when i send a second request(ajax postback) by selecting the mode as sea, the UIViewRoot gets created again for the request in the Restore View Phase. While UIViewRoot creation for this request in Restore View Phase, the tag handlers(<c:xxx>, <f:xxx>, <ui:include>) will get executed.
Since the selected value 'sea' will be updated to model(TestBean.java) only in Update Model Values Phase, in the Restore View Phase the EL #{testBean.selectedMode} will be evaluated to null.
So in my example Test.xhtml page the both tag handlers (<c:if test="#{'sea' eq testBean.selectedMode}"> and <c:if test="#{'air' eq testBean.selectedMode}">) will be evaluated to false. So the UIViewRoot does not contain any information related to the components inside <h:panelGroup id="pageGroup">.
Even though the UIViewRoot does not contain the components information inside the <h:panelGroup id="pageGroup">, How JSF able to render the included pages(<ui:include src="Page1.xhtml" />,<ui:include src="Page2.xhtml" />) successfully?
I am not sure how JSF rendering happening here. While Rendering, Is JSF re-creating the component tree for the particular rendering part(<h:panelGroup id="pageGroup">).?
Note: The example is working fine when i select mode as sea/air. The corresponding parts are rendering correctly.

Processing of JSTL in JSF [duplicate]

I have the below Test.xhtml where i can select mode as Sea/Air. I don't want to load the all the 4 pages(Page1.xhtml, Page2.xhtml, Page3.xhtml, Page4.xhtml) into the jsf view tree. In my scenario, mode once selected and saved that can not be changed to other mode. The saved mode will be shown as view mode. Since at any point of time i need only 2 files (Page1.xhtml, Page2.xhtml (or) Page3.xhtml, Page4.xhtml)... I am using the JSTL tag handler for dynamic including the pages. the below thing is working fine. But i am not able to understand how the rendering working.
Test.xhtml
<!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:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head><title>JSF And JSTL</title>
<link href="../css/styles.css"
rel="stylesheet" type="text/css"/>
</h:head>
<h:body>
<div align="center">
<h:form id="form1">
<h:panelGrid columns="2">
<h:outputLabel value="Select Mode"/>
<h:selectOneMenu id="selectPageId" value="#{testBean.selectedMode}">
<f:selectItem itemLabel="Select" />
<f:selectItem itemLabel="Sea" itemValue="sea" />
<f:selectItem itemLabel="Air" itemValue="air" />
<f:ajax execute="#this" render="pageGroup"/>
</h:selectOneMenu>
</h:panelGrid>
<h:panelGroup id="pageGroup">
<c:if test="#{'sea' eq testBean.selectedMode}">
<ui:include src="Page1.xhtml" />
<ui:include src="Page2.xhtml" />
</c:if>
<c:if test="#{'air' eq testBean.selectedMode}">
<ui:include src="Page3.xhtml" />
<ui:include src="Page4.xhtml" />
</c:if>
</h:panelGroup>
</h:form>
</div>
</h:body>
</html>
TestBean.java
package com.test;
#ManagedBean(name = "testBean")
#ViewScoped
public class TestBean {
private String selectedMode;
public String getSelectedMode() {
return selectedMode;
}
public void setSelectedMode(String selectedMode) {
this.selectedMode = selectedMode;
}
}
Updated Again
Please help to clarify the below.
When i send a request first time to access the Test.xhtml, the jsf view (UIViewRoot) gets created. Since testBean.selectedMode by default is null.. this created view would not contain any component details of <c:if> and <ui:include> which are inside of <h:panelGroup id="pageGroup">.
After that when i send a second request(ajax postback) by selecting the mode as sea, the UIViewRoot gets created again for the request in the Restore View Phase. While UIViewRoot creation for this request in Restore View Phase, the tag handlers(<c:xxx>, <f:xxx>, <ui:include>) will get executed.
Since the selected value 'sea' will be updated to model(TestBean.java) only in Update Model Values Phase, in the Restore View Phase the EL #{testBean.selectedMode} will be evaluated to null.
So in my example Test.xhtml page the both tag handlers (<c:if test="#{'sea' eq testBean.selectedMode}"> and <c:if test="#{'air' eq testBean.selectedMode}">) will be evaluated to false. So the UIViewRoot does not contain any information related to the components inside <h:panelGroup id="pageGroup">.
Even though the UIViewRoot does not contain the components information inside the <h:panelGroup id="pageGroup">, How JSF able to render the included pages(<ui:include src="Page1.xhtml" />,<ui:include src="Page2.xhtml" />) successfully?
I am not sure how JSF rendering happening here. While Rendering, Is JSF re-creating the component tree for the particular rendering part(<h:panelGroup id="pageGroup">).?
Note: The example is working fine when i select mode as sea/air. The corresponding parts are rendering correctly.

JSF2 composite components: are #{cc.childCount} and <composite:insertChildren/> mutually exclusive?

I just dont get it:
If I want my composite component to insert children, I use <composite:insertChildren/> but #{cc.childCount} always returns 0 in that case. On the other hand, If I do not use <composite:insertChildren/> I always get correct childCount without children being rendered. Why is that happening?
All I want to do in my component is to render some "default" panel if there are no children and do not render it in other case - behavior similar to <ui:insert name="param_name">default value</ui:insert>. So I need both insertChildren and childCount which do not seem to work together.
Here is the code:
<my:test>
<h:outputText value="child1" rendered="#{some.trueValue}"/>
<h:outputText value="child2" rendered="#{some.trueValue}"/>
<my:test>
If I use implementation below, I get 2 rendered as expected
<composite:implementation>
<h:outputText value="#{cc.childCount}"/>
</composite:implementation>
When insertChildren is used I get both children rendered and 0 at the end:
<composite:implementation>
<composite:insertChildren/>
<h:outputText value="#{cc.childCount}"/>
</composite:implementation>
Whereas my goal is:
<composite:implementation>
<composite:insertChildren/>
<h:panelGroup rendered="#{cc.childCount == 0}">
some default data
</h:panelGroup>
</composite:implementation>
Any ideas/workarounds?
Put the children in a panelGroup with an id (eg children).
Then
#{component.findComponent('children').childCount}
will give you the correct value. Good luck!
From the cc:insertChildren documentation:
Any child components or template text within the composite component tag in the using page will be re-parented into the composite component at the point indicated by this tag's placement within the section.
So by using <cc:insertChildren> you actually move the children from the #{cc} component to the component in which you specified <cc:insertChildren>, effectively making them (great)* grandchildren of #{cc}. To get easy access to these relocated children, I use a <ui:fragment> as a parent:
<ui:fragment binding="#{childContainer}">
<cc:insertChildren/>
</ui:fragment>
Now you can use #{childContainer.childCount} to get to count the children you specified for your composite component. This solution is a bit fragile though: you can only use your composite component once per view, because of the binding. This problem can of course be solved by binding a FacesComponent bean to your composite component. First the bean:
package com.stackoverflow.jsfinsertchildrenandcountexample;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
#FacesComponent(value = "myCompositeComponent")
public class MyCompositeComponent extends UINamingContainer {
private UIComponent childContainer;
public UIComponent getChildContainer() {
return childContainer;
}
public void setChildContainer(UIComponent childContainer) {
this.childContainer = childContainer;
}
}
And now you can bind this class to your composite component:
<?xml version='1.0' encoding='UTF-8' ?>
<ui:component
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
>
<cc:interface componentType="myCompositeComponent">
</cc:interface>
<cc:implementation>
I have #{cc.childContainer.childCount} children.
<ui:fragment binding="#{cc.childContainer}">
<cc:insertChildren/>
</ui:fragment>
</cc:implementation>
</ui:component>
Now you have a composite component with <cc:insertChildren> and direct access to these children.
EDIT: incorporated BalusC comment.
I had a similar problem. I switched to c:if and it worked, so in your case it would be
<composite:implementation>
<composite:insertChildren/>
<c:if test="#{cc.childCount == 0}">
some default data
</c:if>
</composite:implementation>

Facelet : <ui:insert> cannot be put inside attribute value problem

Im currently experimenting with facelets in JSF 2.0.
I currently have a case where a common grid definition is used by 2 facelets,
differs only in some areas like the bean name, the list name, the title, the grid id.
So what i have in mind is :
create template for that grid, making use of <ui:insert> in the areas that can differ between pages that use it
those 2 pages, make use of the template, supplying the parameters for the template with <ui:define>
Here is my gridTemplate.xhtml :
<?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:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:fn="http://java.sun.com/jsp/jstl/functions">
<h:head>
<title>#{msgs.title}</title>
</h:head>
<h:body>
<ui:composition template="/template/masterlayout.xhtml">
<p:dataTable id="<ui:insert name='gridId' />" var="rpb"
value="#{<ui:insert name='bean' />.<ui:insert name='list' />}">
<f:facet name="header">
<h3><ui:insert name='title' /></h3>
</f:facet>
.....
<f:facet name="footer">
#{fn:length(<ui:insert name='bean' />.<ui:insert name='list' />)} records<br />
</f:facet>
</p:dataTable>
</ui:composition>
</h:body>
</html>
And here's one of the facelet that makes use of the grid template :
<ui:composition template="gridTemplate.xhtml">
<ui:define name="gridId">grid</ui:define>
<ui:define name="bean">myBean</ui:define>
<ui:define name="list">myList</ui:define>
<ui:define name="title">my message !</ui:define>
</ui:composition>
And this experiment ends up with :
javax.servlet.ServletException: Error Parsing /gridTemplate.xhtml: Error Traced[line: 17] The value of attribute "id" associated with an element type "null" must not contain the '<' character.
javax.faces.webapp.FacesServlet.service(FacesServlet.java:325)
root cause
javax.faces.view.facelets.FaceletException: Error Parsing /gridTemplate.xhtml: Error Traced[line: 17] The value of attribute "id" associated with an element type "null" must not contain the '<' character.
com.sun.faces.facelets.compiler.SAXCompiler.doCompile(SAXCompiler.java:394)
com.sun.faces.facelets.compiler.SAXCompiler.doCompile(SAXCompiler.java:368)
com.sun.faces.facelets.compiler.Compiler.compile(Compiler.java:124)
com.sun.faces.facelets.impl.DefaultFaceletFactory.createFacelet(DefaultFaceletFactory.java:297)
com.sun.faces.facelets.impl.DefaultFaceletFactory.access$100(DefaultFaceletFactory.java:92)
com.sun.faces.facelets.impl.DefaultFaceletFactory$1.newInstance(DefaultFaceletFactory.java:162)
com.sun.faces.facelets.impl.DefaultFaceletFactory$1.newInstance(DefaultFaceletFactory.java:161)
com.sun.faces.facelets.impl.DefaultFaceletCache$1.newInstance(DefaultFaceletCache.java:83)
com.sun.faces.facelets.impl.DefaultFaceletCache$1.newInstance(DefaultFaceletCache.java:79)
com.sun.faces.util.ExpiringConcurrentCache$1.call(ExpiringConcurrentCache.java:99)
java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
java.util.concurrent.FutureTask.run(FutureTask.java:138)
I assume that this approach is not plausible, hence there should be a better solution for this kind of needs.
Should i use JSF Facelet Tag or JSF Composite Tag for this kind of case ?
Please share your opinions .. Thank you !
You have to make a composite component instead of template
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface>
<cc:attribute name="list"/>
<cc:attribute name="title"/>
</cc:interface>
<cc:implementation>
<p:dataTable id="#{cc.id}" var="rpb"
value="#{cc.attrs.list}">
<f:facet name="header">
<h3>#{cc.attrs.title}</h3>
</f:facet>
.....
<f:facet name="footer">
#{fn:length(cc.attrs.list)} records<br />
</f:facet>
</p:dataTable>
</cc:implementation>
</html>
This component has to be placed in a folder inside the resources folder, on the root of your webapp. The name of the component will be the same as the filename. So for this example lets call the component myComponent inside the /resources/components folder:
/resources/components/myComponent.xhtml
Then in any page you want to include the component you just have to do include the namespace of your component
xmlns:albert="http://java.sun.com/jsf/composite/components"
and render your component:
<albert:myComponent id="..." title="..." list=#{someBean.someList} />

modalPanel lazy rendering when displayed

Motivation: I want to reduce the size of the page when is accessed, so I thought that lazy rendering on modalPanels would help. The idea is to render the modalPanel when the user clicks the link that displays it.
I want to lazy render on rich:modalPanel when the link to display it is clicked. To achieve this I've found a way:
Code of the modalPanel, wrapped inside a a4j:outputPanel
<a4j:outputPanel id="a4jPanel">
<rich:modalPanel id="panel" rendered="#{bean.renderPanel}">
<!-- here modalPanel things -->
</rich:modalPanel>
</a4j:outputPanel>
Code of the backing bean (session scope):
public boolean isRenderPanel() {
return renderPanel; //default value is false;
}
public void setRenderPanel(boolean value){
this.renderPanel=value;
}
public setRenderFalse(){
this.setRenderPanel(false);
}
Code of the page where it is invoked:
<a4j:form>
<a4j:jsFunction name="setRenderFalse" action="#{user.setRenderFalse}"/>
<a4j:commandLink value="render and show" oncomplete="Richfaces.showModalPanel('panel');setRenderFalse();" reRender="a4jPanel">
<f:setPropertyActionListener target="#{user.renderPanel}" value="true" />
</a4j:commandLink>
</a4j:form>
Problems:
The modalPanel needs to be wrapped inside an a4j:outputPanel because reRendering directly the modalPanel does not work (I never understood why).
After rendering it, an extra request is needed to set the render value to false (the bean is session scoped). Otherwise if we reload the page there would not be any lazy rendering because the value was set to true.
The backing bean has to handle one property to keep the state for each modalPanel, although this property is set to true whenever the link is clicked and set to false when the request is finished. I've tried to keep the rendered state with JS variables but it does not seem to work (they are just read once the page is loaded and never again).
Any more elegant way to do this?
There is a nice solution regarding your question. All is needed is a way to detect postback and couple of xhtmls.
First of all we need a bean that will help with indication of postback
public class HelperBean {
public boolean isPostback() {
FacesContext context = FacesContext.getCurrentInstance();
return context.getRenderKit().getResponseStateManager().isPostback(context);
}
}
empty.xhtml - for a blank content
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
</ui:composition>
modal.xhtml - for wrapping the modal definition
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<rich:modalPanel id="myLazyModal">
<h:outputText value="Modal Content"/>
</rich:modalPanel>
</ui:composition>
lazyModal.xhtml - for handling inclusion of the above xhtmls
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<a4j:include id="lazyModal" layout="block"
viewId="#{helperBean.postback ? 'modal.xhtml' : 'empty.xhtml'}"/>
</ui:composition>
finally use it
<h:form id="frmTest">
<a4j:include id="lazyModalContainer" layout="block" viewId="lazyModal.xhtml"/>
<a4j:commandButton id="btnSubmit" value="Submit" reRender="lazyModalContainer"
oncomplete="Richfaces.showModalPanel('myLazyModal');"/>
</h:form>
Now when the page is loaded empty.xhtml will be included till btnSubmit is clicked.
Regarding to the problems you mentioned (1):
Re-rendering components with the rendered attribute is a bit catchy. When the rendered expression is evaluated to false no markup is sent back to the client. Therefore, supplying the id of the none rendered component to the reRender attribute will never work cause there is no such id on the client side (DOM).
i think you should make separate xhtml(facelet) of modal panel and use ui:include and than on link click the link no need of boolean property than.
enter code here : <ui:include src="modalPanel path">, <a4j:commandLink id="abclink" oncomplete="#{rich:component('yourPanelname')}.show()" reRender="yourPanelForm"/>
Another solution is to set the render attribute of your modalpanel programmatically in the JSF component tree. So you wont't need an additional backing bean which has to handle one property to keep the state for each modalPanel:
Managed Bean:
public void togglePanel(ActionEvent event) {
UIComponent component = event.getComponent();
String forId = (String) component.getAttributes().get("for");
FacesContext currentInstance = FacesContext.getCurrentInstance();
UIComponent findComponent = ComponentFinder.findComponent(currentInstance.getViewRoot(), forId);
findComponent.setRendered(!findComponent.isRendered());
}
View:
Open the panel:
<a4j:commandLink actionListener="#{myBean.togglePanel}" value="Open">
<f:attribute name="for" value="targetPanelId" />
Close the Panel:
<a4j:commandLink id="closePanel" actionListener="#{myBean.togglePanel}" value="someLabel">
<f:attribute name="for" value="targetPanelId" />
The modalpanel:
<a4j:outputPanel ajaxRendered="true">
<rich:modalPanel id="targetPanelId" width="800" height="500" rendered="false" showWhenRendered="true">

Resources