1

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.

moneyt
  • 452
  • 8
  • 18

1 Answers1

2

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}" />

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Please see my edits. Do they change your answer at all? Could this be an issue with primefaces ajax implementation? – moneyt Nov 22 '13 at 14:16
  • What about the case where it would be desirable to update the component and other components on the page ()? I presume there isn't a .replace and .contains el function (and it would be messy even if there was). Would this case be solvable by having a String property in the composite component backing bean that generates an appropriate string or is that not available at the right time in the JSF lifecycle? – moneyt Nov 22 '13 at 15:11
  • Hmm.. This solution still doesn't seem to work. I try it and I still get the same exception: Cannot find component with identifier ":j_idt157:0:j_idt163:j_idt171:j_idt386:myId" referenced from "j_idt157:0:j_idt163:j_idt171:j_idt386:myId:note-history:0:j_idt408". Could it be the fact that this composite component is in a p:dataGrid (and for that matter, nested in another composite component) on the main page? I know I've had issues with p:data* components and ajax ids in the past. – moneyt Nov 22 '13 at 15:24
  • To answer my own question from the previous comment, yes, it does appear to be an issue with how things are laid out on the main page as your solution does work if I place the component at the root level of a test page. Answer marked accepted, but I'm happy to hear any thoughts you have on solving the issue with it working within a p:data* component. – moneyt Nov 22 '13 at 15:35
  • This is indeed painful. I'd expect PrimeFaces specific `@parent` to come into rescue as in `update="@parent"` in client and then just `update="#{cc.attrs.update}"` in composite, but when I tested it, it appears to not work in this construct for some unclear reason. I didn't have time/mood to debug that. – BalusC Nov 22 '13 at 15:39