Search code examples
jsfprimefacescomposite-component

Referencing components inside of composite components (and vice versa)


I'm having one or two problems with Primefaces (v5.2):

Referencing components inside composite components

Let's say I have a composite component that wraps an inputfield:

myinputfield.xhtml

<composite:interface>
    <composite:attribute name="value" />
    ...
</composite:interface>

<composite:implementation>
    <h:inputText value="#{cc.attrs.value}" />
</composite:implementation>

(Of course the real application does "a little" more.)

In my page I now use this field like this:

index.xhtml

<my:myinputputfield value=#{controller.inputstring} />

This works. But: Know I want to reference that inner inputfield from outside, for example for labels or messages. Something like:

<p:inputLabel for="mif" value="Your Input:"/>
<my:myinputputfield id="mif" value=#{controller.inputstring} />
<p:message for="mif" />

Of course that doesn't work, because id isn't defined for myinputfield. So the first idea that pops to mind is to extent the cc like this:

myinputfield.xhtml (new)

<composite:interface>
    <composite:attribute name="id" />
    <composite:attribute name="value" />
    ...
</composite:interface>

<composite:implementation>
    <h:inputText id="{cc.attrs.id}" value="#{cc.attrs.value}" />
</composite:implementation>

Which does not work as well. I tried different things and read different answers and articles without finding an answer to this.


The second problem is the complete opposite:

Referencing components outside composite components

This time imagine it the other way around. I have a customized label, message or in my case a tooltip:

mytooltip.xhtml

<composite:interface>
    <composite:attribute name="for" />
    <composite:attribute name="value" />
    ...
</composite:interface>

<composite:implementation>
    <p:toolTip for="#{cc.attrs.for}" value="#{cc.attrs.value}" />
</composite:implementation>

This time I want to attach mytooltip to an existing component:

index.xhtml

<h:outputtext id="ot" value="Hello World!" />
<my:mytooltip for="ot" value="since 1974" />

Which also does not work. (Of course!?)

This problem I had some time ago and solved it by inclduing the outputText in the composite component.


But I have the feeling it should be possible to manage both user cases. But how?


Solution

    1. Referencing components inside composite components

      give the internal input a static id

      <composite:interface>
          <composite:attribute name="value" />
          ...
      </composite:interface>
      
      <composite:implementation>
          <h:inputText id="input" value="#{cc.attrs.value}" />
      </composite:implementation>
      

      reference the internal component as with any naming container:

      <p:inputLabel for="mif:input" value="Your Input:"/>
      <my:myinputputfield id="mif" value=#{controller.inputstring} />
      <p:message for="mif:input" />
      


    1. Referencing components outside composite components

      the canonical way is to use the full client id:

      <h:form id="form">
          <h:outputText id="ot" value="Hello World!" />
          <my:mytooltip for=":form:ot" value="since 1974" />
      </h:form>
      

      but, since you are passing the search expression to a PF component, you can also:

      <h:form>
          <h:outputText id="ot" value="Hello World!" />
          <my:mytooltip for="@form:ot" value="since 1974" />
      </h:form>
      

      or generically:

      <p:tabView>
          <p:tab title="random tab">
              <h:outputText id="ot" value="Hello World!" />
              <my:mytooltip for="@namingcontainer:ot" value="since 1974" />
          </p:tab>
      </p:tabView>
      

      or even:

      <h:outputText value="Hello World!" />
      <my:mytooltip for="@composite:@previous" value="since 1974" />
      

      however, in such cases, a tag-component/facelet-tag-file could be a better approach.