#{cc.clientId} evaluated in wrong composite after upgrading to JSF 2.2
Asked Answered
D

1

2

I have a tag library which was written in JSF 2.0 + PrimeFaces 3.4, now i am trying to update to JSF 2.2 and PrimeFaces 4.0. But i realized that the value of attributes passed to component evaluated in composite component and it leads to wrong id for rendering.

enum.xhtml (composite component)

<cc:interface>
           <cc:attribute name="render" default="@this"/>
            .....
</cc:interface>
<cc:implementation>
  <h:selectOneMenu ......../>
  <p:ajax update="#{cc.attrs.render}" process="#{cc.attrs.execute}" />  
</cc:implementation>

usage :

<t:enum id="authenticationSource" value="#{authenticationStrategy}" .....
  render=":#{cc.clientId}:tabView:passwordVisibility"/>

render attribute value which is :#{cc.clientId}:tabView:passwordVisibility, should be

:j_idt1:j_idt3:j_idt5:editorDialog:j_idt39:j_idt40:tabView:passwordVisibility`

But it is evaluated as

:j_idt1:j_idt3:j_idt5:editorDialog:j_idt39:j_idt40:tabView:autheticationSource:tabView:passwordVisibility

Attribute value of render is evaluated in composite component and it caused to error. It should be evaluated where it is used and it was like that in JSF 2.0. Is there any configuration property or anything to overcome this error.

I'm using wildfly 8.1.0-Final

Dap answered 24/9, 2014 at 12:59 Comment(1)
Perhaps any elements around t:enum have become a naming container? Is there an element with ID autheticationSource?Recede
G
4

This composite is not rightly designed. You're not supposed to use #{cc.clientId} outside the composite's context. More generally, you're not supposed to know anything about the composite's internals from outside the composite. The composite itself should worry about this.

This construct will fail if you're nesting composite components in each other. The #{cc} would then actually refer the "current" composite component. Perhaps you were relying on a bug in an older JSF implementation where the #{cc} scope isn't properly being cleared out after the nested composite component (i.e. it would refer the last assigned value instead of the value available in the current context).

Perhaps you're victim of overusing composite components for the wrong puspose only and only because of the zero-configuration nature as compared to regular tagfiles/includes. For detail as to when exactly to use the one or other, head to When to use <ui:include>, tag files, composite components and/or custom components? To the point, use a composite only and only if you want to bind a bunch of closely related components to a single bean property, and thus certainly not to a "whole" bean with several properties.

If you're absolutely positive that a composite is the right solution for your requirement, and/or you've refactored the composite accordingly to eliminate the mentioned misuse, then there are 2 possible approaches for applying client behavior on the composite component, depending on the concrete functional requirement (you can even combine the both ways if necessary).

  1. If you want to let the composite ajax-render a component outside the composite, externalize <p:ajax> (or <f:ajax>) as <cc:clientBehavior>:

    <cc:interface>
        <cc:clientBehavior name="myCustomEventName" targets="idOfTargetComponent" event="valueChange" />
        ...
    </cc:interface>
    <cc:implementation>
        <h:selectOneMenu id="idOfTargetComponent" ...>
            <f:selectItems ... />
        </h:selectOneMenu>
    </cc:implementation>
    

    Which is to be used as:

    <t:enum ...>
        <p:ajax event="myCustomEventName" update=":absoluteClientIdOfComponentOUTSIDEComposite" />
    </t:enum>
    <x:someComponent id="idOfComponentOUTSIDEComposite" />
    
  2. If you want to let the composite ajax-render a component inside the composite, then let the composite do it all by itself.

    <cc:interface>
        ...
    </cc:interface>
    <cc:implementation>
        <h:selectOneMenu ...>
            <f:selectItems ... />
            <p:ajax update="idOfComponentINSIDEComposite" />
        </h:selectOneMenu>
        <x:someComponent id="idOfComponentINSIDEComposite" />
    </cc:implementation>
    

    And use it the usual way:

    <t:enum ... />
    
Greed answered 22/10, 2014 at 12:53 Comment(2)
For me it's not about zero configuration. It's about being about able to define an interface, instead of a bunch of ui:params that don't even work for proper method expressions. I miss client behaviors, action sources and value holders in facelet templates.Kendra
Method expressions and action sources: this problem is known, use <o:methodParam>. Client behaviors: just use <ui:define/insert>. Value holders: should just work.Greed

© 2022 - 2024 — McMap. All rights reserved.