How to use EL with <ui:repeat var> in id attribute of a JSF component
Asked Answered
R

1

11

I have following code:

<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
  <h:form id="#{class2.name}"> 
    <h:outputText value="#{class2.name}" />
  </h:form>
</ui:repeat>

However, when I open the page, it errors as follows:

component identifier must not be a zero-length String

But it is properly printed in the <h:outputText>. How is this caused and how can I solve it?

Rombert answered 21/8, 2013 at 13:20 Comment(6)
The question is, do you really need that? Why are you creating multiple forms?Glossator
hi, yep, i have for each row one form, to get sure that only this row is persisted (next to the outputtext a button will be included)Rombert
...but i you tell me that EL doesn't work for ids, i will try to search another wayRombert
See this answer #15114586Autotoxin
Considering that ui:repeat works in page rendering time and c:forEach while page building, I would recommend you using the second one.Glossator
normally c:foreach would do that, but then it doen't rerender after ajax-calls...Rombert
W
25

You can use EL in the id attribute of a JSF component, but the EL variable has to be available during view build time, while the JSF component tree is to be built. However, the <ui:repeat> runs during view render time, while the HTML output is to be generated based on JSF component tree. The <ui:repeat var> is not available during view build time and #{class2.name} evaluates to null which totally explains the error you got. That it works in <h:outputText> is because it runs during view render time.

If you replace <ui:repeat> by <c:forEach>, which runs during view build time, then it'll work as you intented. The <c:forEach> will namely generate physically multiple <h:form> components in the JSF component tree which each generate individually their own HTML output (in contrary to <ui:repeat>, wherein the very same <h:form> component is been reused multiple times to generate HTML output).

<c:forEach var="class2" items="#{bean.list}" varStatus="status">
  <h:form id="#{class2.name}"> 
    <h:outputText value="#{class2.name}" />
  </h:form>
</c:forEach>

However, I really wonder why you need to do that. There's usually no need to dynamically assign component IDs. JSF will already ensure the uniqueness of the ID. The below example,

<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
  <h:form id="form"> 
    <h:outputText value="#{class2.name}" />
  </h:form>
</ui:repeat>

will end up in multiple forms with each an unique ID, suffixed with iteration index of the <ui:repeat>. If you actually need to use #{class2.name} for some JavaScript/jQuery purposes (you did nowhere state the concrete functional requirement in the question for which you thought that this would be the right solution, so it's merely guessing), then just wrap it in a plain vanilla HTML element:

<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
  <div id="#{class2.name}"> 
    <h:form id="form">
      <h:outputText value="#{class2.name}" />
    </h:form>
  </div>
</ui:repeat>

Or set it as style class of a JSF component, which is also just selectable via a CSS selector:

<ui:repeat var="class2" value="#{bean.list}" varStatus="status">
  <h:form id="form" styleClass="#{class2.name}">
    <h:outputText value="#{class2.name}" />
  </h:form>
</ui:repeat>

See also:

Wellordered answered 21/8, 2013 at 13:36 Comment(1)
This issue drove me crazy. +1 for a clean explanation of WHY it didn't work and not just providing a solution to the issue. In my case, the multiple forms would work but they wouldn't submit the form data inside correctly (only the last table would). Data wasn't set and I kept getting null there. This actually fixed the issue. Thanks! So remember, if you have multiple forms created in a ui:repeat and the data in the forms are null, read @BalusC's post and +1 his solution!;)Paez

© 2022 - 2024 — McMap. All rights reserved.