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 <composi开发者_StackOverflow社区te: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>
精彩评论