0
votes

I'm creating a composite component in JSF 2..., inside it I have defined a controller attribute which should point to a custom object which handles the logic behind the component.

Basically the component has a dropdownMenu that's created dynamically based on some options provided by the controller.

I tried something like this:

<composite:interface>
    <composite:attribute name="id" required="true" />
    <composite:attribute name="controller" required="true"/>
</composite:interface>

<composite:implementation>

    <a4j:outputPanel layout="block" id="pnlTaskOptions" style="width:300px;">
        
        <rich:dropDownMenu mode="ajax">
            <f:facet name="label">
               <h:panelGroup layout="block" styleClass="botonA" rendered="true">
                    <h:outputLink styleClass="solicitarAutorizacionA"
                        value="#{msg_autweb['etiqueta.aprobar']}"
                        immediate="true"/>
                </h:panelGroup>
            </f:facet>
            <a4j:repeat value="#{cc.attrs.controller.taskOptions}" var="option">
                <rich:menuItem  label="opcion"
                                action="#{cc.attrs.controller.executeOption(option)}"
                                render="pnlTaskOptions">
            </rich:menuItem>
            </a4j:repeat>
        </rich:dropDownMenu>
        
    </a4j:outputPanel>

</composite:implementation>

cc.attrs.controller.taskOptions is an String arrayList that's filled inside the controller's constructor.

I have debugged the getters of it, and checked that the array was being retrieved correctly, in other words checked that it wasn't empty.

However the menu didn't appear, like if there were no children menu items. What's going on? Isn't possible to use a a4j:repeat inside a composite component?

2
Does it work outside the composite then? If I'm not wrong, you should have used <c:forEach> instead. - BalusC
Mmm when I use c:forEach like this: <c:forEach items="#{cc.attrs.controller.taskOptions}" var="option"> <rich:menuItem label="opcion" action="#{cc.attrs.controller.executeOption(option)}" render="pnlTaskOptions"> </rich:menuItem> </c:forEach> It doesn't even call the getter for the items attribute... - Pablo
I don't use RichFaces closely, but if I'm not wrong, using ui:repeat to generate rich:menuItem wouldn't even have worked outside the composite. You should have used the c:forEach for that. But inside the composite itself, the c:forEach in turn wouldn't work when the items attribute depends on JSF render time attribute (e.g. obtained from the var of another parent ui:repeat or h:dataTable). I think a tag file is for you after all easier. - BalusC
I'm sorry BalusC, I made a mistake and got a null pointer exception when trying the c:forEach inside the component. However when I realized this it worked... althought I haven't tested using ajax render attributes on my component. Also something I noticed is that the getter is called 4 times when using c:forEach... why is this? And why I can't use a4j:repeat or ui:repeat inside a composite?. Anyways thanks a lot BalusC, you really helped me. Thanks. - Pablo

2 Answers

1
votes

The <a4j:repeat> and <ui:repeat> are UI components which runs at JSF HTML render time. All its children will generate HTML multiple times as many times as the component needs to iterate over the supplied collection. Note that there's physically only one component in the JSF component tree. The <c:forEach> is a tag file which runs at JSF view build time. The JSF component tree will end up with as many duplicated children as the tag needs to iterate over the supplied collection. Each of those duplicated children generates HTML only once.

In your initial approach, you end up with <rich:dropdownMenu> which has only one child of type <a4j:repeat> which in turn has only one child of <rich:menuItem>. This is not supported by <rich:dropdownMenu>. This component supports only multiple children of type <rich:menuItem>.

So, replace <a4j:repeat> by <c:forEach> and it should work.

You only need to ensure that its value in turn should not depend on the value of some parent iterating UI component, or it will fail again. In such case, you really need a tag file instead of a composite component.

See also:

1
votes

As long as i know an attribute being used in an 'action' tag-attribute must be defined as a method in the composite interface; i.e using the tag-attribute 'method-signature' in the definition of the composite component attribute.