Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
674 views
in Technique[技术] by (71.8m points)

jsf 2 - Refer to JSF dynamically generated ids based on iteration index

In JSF, <ui:repeat/> and similar components such as PrimeFaces <p:dataTable/> generate dynamic ids for sub-components based on the iteration index, i.e.:

<p:dataTable id="table" var="item" value="#{itemList}">
    <h:outputText id="name" value="#{item.name}"/>
</p:dataTable>

will generate something like this:

<table id="table">
    <span id="table:0:name">name0</span>
    <span id="table:1:name">name1</span>
    <span id="table:2:name">name2</span>
    ...
    <span id="table:n:name">nameN</span>
</table>

so all the elements clearly have a distinct client id. I intentionally skipped the <tr/>, <td/>, etc.

So, <p:ajax ... update=":table:name"/> refers to all the names in the table and it works fine, <p:ajax ... update=":table:#{someDesiredIndex}:name"/> fails with a message similar to SEVERE: javax.faces.FacesException: Cannot find component with identifier ":table:0:name" in view. event though I can confirm that the component exists in the markup. Is it not possible to do this?

I'm running on GlassFish 3.1.2 and Mojarra 2.1.6 in case it is relevant.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It does indeed not exist in the JSF component tree as traversable by UIViewRoot#findComponent(). It exists only in the generated HTML output. There's only one <h:outputText id="name"> in the JSF component tree, not multiple as you seemed to expect. It's just been reused multiple times when producing the HTML output. At best, you can get the physical component by table:name, but this does in turn not exist in the HTML DOM tree, so the document.getElementById() would fail on that during performing the ajax update.

In order to achieve the concrete functional requirement anyway, you basically need to have a physical existing component representing the row in the JSF component tree. You can create them in a loop if you use a view build time tag, such as JSTL <c:forEach>, instead of a view render time tag.

<table id="table">
    <c:forEach items="#{itemList}" var="item" varStatus="loop">
        <tr><td><h:outputText id="table_#{loop.index}_name" value="#{item.name}" /></td></tr>
    </c:forEach>
</table>

This will create physically multiple components in the JSF component tree and this get rendered as:

<table id="table">
    <span id="table_0_name">name0</span>
    <span id="table_1_name">name1</span>
    <span id="table_2_name">name2</span>
    ...
    <span id="table_n_name">nameN</span>
</table>

And you can reference them via e.g. update=":table_#{someDesiredIndex}_name".

See also:


Update: since Mojarra 2.2.5, the <f:ajax> doesn't validate the client ID anymore and the renderer is capable of walking through iterating components in order to find the right iteration round to render. So referencing the iteration index in <f:ajax> this way should just work fine. It only doesn't work yet in current MyFaces 2.2.7 / PrimeFaces 5.1 versions, but it's expected that they will catch up it in a future version.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...