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

3

8

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?

Catinacation answered 16/6, 2011 at 19:36 Comment(0)
B
6

Put the children in a panelGroup with an id (eg children).

Then

#{component.findComponent('children').childCount}

will give you the correct value. Good luck!

Bourdon answered 16/6, 2011 at 22:27 Comment(0)
C
4

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.

Cartage answered 24/7, 2015 at 20:37 Comment(1)
getFamily() is unnecessary as that's already implemented in UINamingContainer. Only when you implement NamingContainer interface, or another UIComponent subclass, you indeed have to specify yourself.Shrunk
R
2

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>
Revision answered 26/4, 2012 at 8:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.