Search code examples
jsf-2faceletsmyfacescomposite-component

addressing components from other naming containers


What I would like to achieve is to be able to address some JSF components from within other naming container.

Usecase: composite component which encapsulates some features using - for a field which is defined out of the composite component.

Some code:

<form id="foo">
...
<label for="nameTxt">Name:</label>
<component:customError forField="nameTxt" />
<h:inputText id="nameTxt" />
...
</form>

and the component:

<composite:implementation>
    <h:panelGroup id="errorComponent">
    ...
    <h:message for="#{cc.attrs.forField}" id="errorMsg" style="display:none;" />
    ...
    </h:panelGroup>
</composite:implementation>

The problem is that on rendering the message I get:

Could not render Message. Unable to find component 'nameTxt' (calling findComponent on component 'j_id963445801_469fc056:errorMsg')

I think I understand that the problem lies in the fact the the field "nameTxt" and the message "errorMsg" lie in other naming-containers. So what I would have and like to do is to specify the path/id of "nameTxt" in relation to some common ancestor.

After studying shortly the algorithm UIComponentBase:findComponent I do not actually see any other way of adressing cross naming-containers than by specyfing whole (absolute) id-path from the root (i.e. ":foo:...:nameTxt"). And this is both clumsy and prone for errors after changing the page structure.

So - how to address properly the field "nameTxt" from within the message in the composite component?


Solution

  • I can reproduce your problem on MyFaces 2.1.3, but not on Mojarra 2.1.4 (and also not on older Mojarra 2.0.2). This is likely a bug in MyFaces, you'd need to report it to the MyFaces guys. In the meanwhile, I don't see any other option than (temporarily) replacing the JSF implementation by Mojarra. It has however its own share of issues as well, mainly with its broken <ui:repeat> and partial state saving implementations.


    Update: I found a workaround, it's however a bit clumsy:

    <component:customError forField=":#{nameTxt.clientId}" />
    <h:inputText id="nameTxt" binding="#{nameTxt}" />
    

    This will lookup using the absolute client ID instead of relative client ID. You'd only need to remove style="display:none" from your <h:message> to solve a different matter.