Question

I use component binding and clientId property to find components in my javascript code, something like this:

<div data-for="@parent" data-example="#{componentBindings.myInput.clientId}" />
...
<h:inputText binding="#{componentBindings.myInput}" value="#{myBean.myProperty}" />

The only problem with this, is that jsf sometimes doesnt render the id attribute of the inputText field, so even if i know the clientId in the js code, i cant find the element as it doesnt have the id attribute on the client side.

  1. Whats the requirement for rendering the autogenerated id tag?
  2. Is there any way to make jsf render all the cliendId-s automatically? (like a faces-config entry)
  3. Is there any way to ensure that the clientId of an individual element is rendered (without explicitly giving it an id)?

update

i found my way around using a trick:

<span id="#{componentBindings.myInput.clientId}">
    <h:inputText binding="#{componentBindings.myInput}" value="#{myBean.myProperty}" />
</span>

Although in this case there are better ways, like using name as suggested, if its not an input field you wish to render with ajax, it can be useful. For example with facets:

<!-- myComponent.xhtml -->
<!-- ... -->
<cc:interface>
    <cc:facet name="someFacet" required="true" />
</cc:interface>
<cc:implementation>
    <h:form>
        <cc:renderFacet name="someFacet"/>
    </h:form>
</cc:implementation>

<!-- page.xhtml -->
<!-- ... -->
<my:myComponent>
    <f:facet name="someFacet">
        <h:inputText />
        <!-- ... -->
    </f:facet>
</my:myComponent>

If you look at the component tree, you will see, that the facet is not nested in the form, its a direct child of the cc. So it would be nice to execute the cc with ajax:

<!-- myComponent.xhtml -->
<!-- ... -->
<cc:interface>
    <cc:facet name="someFacet" required="true" />
</cc:interface>
<cc:implementation>
    <h:form>
        <cc:renderFacet name="someFacet"/>
        <h:commandLink>
            <f:ajax execute=":#{cc.clientId}" render=":#{cc.clientId}" />
        </h:commandLink>
    </h:form>
</cc:implementation>

but this will throw a malformed xml exception, as there is no element with the requested id on the client side. With this trick, you can make it work:

<cc:interface>
    <cc:facet name="someFacet" required="true" preferred="true"/>
</cc:interface>

<cc:implementation>
    <span id=#{cc.clientId}>
        <h:form>
            <cc:renderFacet name="someFacet"/>
            <h:commandLink>
                <f:ajax execute=":#{cc.clientId}" render=":#{cc.clientId}" />
            </h:commandLink>
        </h:form>
    </span>
</cc:implementation>

I am probably not the first one who found this workaround, but i decided to share it as i didnt find it anywhere

Was it helpful?

Solution

From Mojarra's HtmlBasicRenderer source code (starting at line 672 in 2.1.17):

/**
 * @param component the component of interest
 *
 * @return true if this renderer should render an id attribute.
 */
protected boolean shouldWriteIdAttribute(UIComponent component) {

    // By default we only write the id attribute if:
    //
    // - We have a non-auto-generated id, or...
    // - We have client behaviors.
    //
    // We assume that if client behaviors are present, they
    // may need access to the id (AjaxBehavior certainly does).

    String id;
    return (null != (id = component.getId()) &&
                (!id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX) ||
                    ((component instanceof ClientBehaviorHolder) &&
                      !((ClientBehaviorHolder)component).getClientBehaviors().isEmpty())));
}

So, it only renders the HTML element's id attribute if the component has an id attribute set or if the component has client behavior holders (read: <f:ajax> children).

So, you really need to specify it yourself:

<h:inputText id="foo" ... />

Otherwise, change your JS code so that it selects inputs by their name instead of id.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top