jsf-2jstlcomposite-componentnaming-containers

JSTL, composite, NamingContainer and prependId


I have written a composite component and want to use the reserved EL #{component.clientId} to make a JQuery bind. To use this retrieved clientId in a another place in the page (outside the component), I use JSTL to store it in a view scope variable. The strange thing is that JSTL seems to prevent the natural composite component behavior of appending its id in front of its children (NamingContainer behavior). I know that JSTL is a little tricky, interfering with other components (ui:repeat for instance) because of lifecycle things, but here I don't understand this behavior.

Some concrete code is better than this long speech:

<html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:rich="http://richfaces.org/rich"
        xmlns:c="http://java.sun.com/jsp/jstl/core"    
        xmlns:composite="http://java.sun.com/jsf/composite">
    <composite:interface>
        […]
    </composite:interface>
    <composite:implementation>
        <rich:dataTable id="mySoLovedDataTable" […]>
            #{component.clientId}
            <!-- Commenting or uncommenting this line make the whole point -->
            <c:set var="targetClientId" value="#{component.clientId}" scope="view" />
            […]
        </rich:dataTable>
    </composite:implementation>
</html>

With the line commented on, #{component.clientId} gives something like j_idt261:mySoLovedDataTable.

With the line commented out, it gives just mySoLovedDataTable.


Solution

  • JSTL runs during view build time. It runs at the point that JSF parses the view template into a fullworthy and renderable JSF component tree. JSF runs during view render time. It runs at the point that JSF encodes the component tree into a bunch of HTML. You can visualize it as follows: JSTL runs from top to bottom first and produces a result with JSF tags only. Then, during JSF render response phase, JSF will run from top to bottom and produce HTML result.

    In other words JSTL and JSF doesn't run in sync as you'd expect from the coding. Usually you would like to use Facelets' <ui:param> instead of JSTL <c:set>.

    <ui:param name="targetClientId" value="#{component.clientId}" />
    

    Note that this does not really set anything in any scope. It merely creates kind of an "alias" for the given expression. I'm not sure if it works in your particular case the way as you intend, but as far I understand the functional requirement, you'd like to be able to obtain the client ID <rich:dataTable> further in the view, after the component. In that case, better use binding:

    <rich:dataTable binding="#{table}" ...>
        ...
    </rich:dataTable>
    
    <script>
        var $table = jQuery("[id='#{table.clientId}']");
        // ...
    </script>