1

I have the below case:

<p:selectBooleanCheckbox id="couponAgreement">
<h:panelGroup rendered="#{!couponAgreement.valid}">

Is it possible to get an component by its id? Setting the binding would do it, but when I include this multiple times in my JSF page, only the last instance is rendered.

I imagine something like this:

<h:panelGroup rendered="#{!fn:getComponentById('couponAgreement').valid}">
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
wutzebaer
  • 14,365
  • 19
  • 99
  • 170
  • `` and then in EL `#{component.clientId}`. – Tiny Aug 21 '15 at 15:07
  • as i said, binding doesn't work because i include it multiple times.. by the way i dont need the clientid – wutzebaer Aug 21 '15 at 15:08
  • Components can be identified uniquely, if inclusion is done within a naming container like ``. – Tiny Aug 21 '15 at 15:14
  • Is there any reason you do not want to do this via an ajax update, setting some boolean in your backing bean, and using that as your `rendered` condition? – cobaltduck Aug 21 '15 at 15:21
  • @Tiny i surrounded the include tag with a subview tag, but it has the same result: only the last checkbox is shown – wutzebaer Aug 24 '15 at 07:27
  • @cobaltduck yes: the "rendered" should change in subject to a value even if the validation fails - and if validation fails the value is not transferred to the bean – wutzebaer Aug 24 '15 at 07:30
  • @wuztebaer- Ah. Got it. In that case, I would have suggested `immediate="true"` but my past experience is that even this doesn't always work, and might even have the opposite effect of what you want when the input is invalid. One of the reasons I basically gave up on client-side validation long ago. I'm glad you did get the answer you needed. – cobaltduck Aug 24 '15 at 12:26
  • @cobaltduck immediate="true" seems to work as expected only for command links/buttons ... inputs seem to behave differently – wutzebaer Aug 24 '15 at 14:47

1 Answers1

3

You can use UIComponent#findComponent() for this. It will be searched relative to the naming container parent. So if you can guarantee that those components have an unique naming container parent (e.g. <ui:repeat>, <f:subview>, <h:form>, etc), then do so:

<h:someInput id="someInput" ... />
<h:someOutput ... rendered="#{component.findComponent('someInput').valid}" />

As to binding, you should just make sure that the value of the binding attribute is exclusively tied to the component itself, and not shared across multiple components.

So, this is wrong when it concerns a component in a reusable include/tagfile/composite:

<h:someInput binding="#{someInput}" ... />
<h:someOutput ... rendered="#{someInput.valid}" />

Rather bind it to an unique key. Let the include/tagfile/composite require a id param/attribute and then use <c:set> to create a variable which appends the id so that you can ultimately use it as key of request scope map.

<c:set var="binding" value="binding_someInput_#{id}" />
<h:someInput id="#{id}" binding="#{requestScope[binding]}" ... />
<h:someOutput ... rendered="#{requestScope[binding].valid}" />

To keep the request scope clean, consider creating a hash map in request scope via faces-config.xml:

<managed-bean>
    <description>Holder of all component bindings.</description>
    <managed-bean-name>components</managed-bean-name>
    <managed-bean-class>java.util.HashMap</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<h:someInput id="#{id}" binding="#{components[id]}" ... />
<h:someOutput ... rendered="#{components[id].valid}" />

In case of composite components, there's another way. Bind it to the backing component.

<cc:interface componentType="someComposite">
    ...
</cc:interface>
<cc:implementation>
    <h:someInput binding="#{cc.someInput}" ... />
    <h:someOutput ... rendered="#{cc.someInput.valid}" />
</cc:implementation>
@FacesComponent("someComposite")
public class SomeComposite extends UINamingContainer {

    private UIInput someInput; // +getter+setter

    // ...
}

In a decently designed composite you often already have or ultimately need it anyway.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • thanks for this detailed example! It seems that "cc.findComponent('couponAgreement').valid" does the job too, what do you think? The include seems to be handled as cc context – wutzebaer Aug 24 '15 at 08:01
  • You can indeed do so. – BalusC Aug 24 '15 at 08:03