Question

I have a number of composite components in my application, and from time to time, I need to reference those components as a whole. By default, composite components don't generate any additional mark up over their child components, so if you give a composite component an id attribute, it simply appends that id to the generated ids of the child components. I would like to reference the whole component by id (i.e. for use in jquery manipulation). JSF components do not allow EL in the id attribute, so to accomplish this, I have so far been wrapping my composite components in plain html divs as follows:

<ui:component ...
xmlns:cc="http://java.sun.com/jsf/composite"
...>

<cc:interface componentType="myComponent">
    <cc:attribute name="process" type="java.lang.String" required="false" default="@form"/>
    <cc:attribute name="update" type="java.lang.String" required="false" default="@form"/>  
...
</cc:interface>

<cc:implementation>
    <h:outputStylesheet name="myComponent.css" library="css/components" target="head"/>
    <div id="#{cc.clientId}" class="myComponent #{cc.attrs.styleClass}">
        ... composite component implementation here ...     

    <p:dataTable id="note-history" styleClass="entity-notes-history" value="#{cc.notes}" var="note" paginator="true" paginatorAlwaysVisible="false" 
                                paginatorPosition="bottom" rows="5" rendered="#{not empty cc.notes}" rowStyleClass="#{note.noteBaseType.word}">
            ...

            <p:column rendered="#{note.noteBaseType == 'Warning' and not cc.attrs.displayOnly}" style="width: 8em;">
                <p:commandButton actionListener="#{cc.cancelSeverity(note.id)}" process="#{cc.attrs.process}" update="#{cc.attrs.update} update="note-history" value="Reduce Severity"/>
            </p:column>
        </p:dataTable>
    </div>

</cc:implementation>

And the component would be used as follows:

....
<ppa:myComponent id="myId" update="myId" />
....

Unfortunately, if I have any ajax (specifically p:ajax from primefaces) calls within the composite component, when I try to have them update the composite component by ID I get a "Cannot find component with identifier" exception. Is there any way to get JSF to auto generate a valid component and container for my composite components? I would really hate to have to generate two containing divs or spans (i.e. adding an h:panelGroup around the plain html div) just to get ajax working. Alternatively, is there a way to force JSF to allow dynamic ids on components?

Edit:

In response to BallusC's answer, I've edited the example code to be more clear in how the component is built and used, since given his answer it's entirely possible I'm just doing something that isn't allowed.

Was it helpful?

Solution

when I try to have them update the composite component by ID I get a "Cannot find component with identifier" exception

It's unfortunate that you didn't show how exactly you did that, because it should work just fine, provided that you used the composite's client ID in the update which is enclosed in the composite itself:

<f:ajax render=":#{cc.clientId}" />

Or, that you just used composite's own ID in the update which is performed outside the composite inside the same naming container parent:

<f:ajax render="myId" />

Most likely you did it the wrong way. E.g. you forgot the : prefix, or you used #{cc.id} instead of #{cc.clientId}, etc. The above has as far as I know always worked in all Mojarra 2.x versions released so far.

See also:


Alternatively, is there a way to force JSF to allow dynamic ids on components?

You can just use EL in id attribute, provided that it's available during view build time and thus not only during view render time. See also JSTL in JSF2 Facelets... makes sense? for related background information.


Update as per your question update wherein you finally show how you tried to achieve it;

With

<ppa:myComponent id="myId" update="myId" />

and

<p:commandButton ... update="#{cc.attrs.update}" />

you're effectively ultimately doing a

<p:commandButton ... update="myId" />

which is basically looking for a component with ID myId inside the context of the composite itself! It would only have effect on e.g. a <h:outputText id="myID"> next to the <p:commandButton> inside the same <cc:implementation>.

The functional requirement is understood, but the solution is not so nice. Your closest bet is (ab)using @this and conditionally checking for that in update:

<ppa:myComponent id="myId" update="@this" />

with something like this

<p:commandButton ... update="#{cc.attrs.update == '@this' ? ':'.concat(cc.clientId) : cc.attrs.update}" />

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top