How to append loop index of c:forEach tag to Struts HTML tag attributes?

Salman Paracha picture Salman Paracha · May 11, 2011 · Viewed 18.2k times · Source

How can I append the loop index of a c:forEach tag to the attributes of a struts select/text tag?

For example.

<%@ taglib uri="/WEB-INF/tld/struts-html.tld" prefix="html"%>

<c:forEach begin="2" end="${pageView.guestCount}" varStatus="gC">
    <div class="section guest-details">
       <html:select property='title_guest<c:out value="${gC.index}"/>'>
          <html:options collection="titles" property="code" labelProperty="value" />
       </html:select>
    </div>
 </c:forEach>

throws the following error

javax.servlet.jsp.JspException at org.apache.struts.taglib.html.SelectTag.calculateMatchValues(SelectTag.java:246)

Now, when I debug the code at <html:select ... it shows that when the property attribute it set, its set as "title_guest<c:out value="${gC.index}"/>" which could be the cause of the exception above.

Also, I should mention, that if I use the above format for appending the loop index to a standard html tag attribute like a <select> tag, the code works fine.

For example

<c:forEach begin="2" end="${pageView.guestCount}" varStatus="gC">
  <div class="section guest-details">
      <select name='title_guest<c:out value="${gC.index }"/>'>
            <option value="">Select Title</option>
      </select>
  </div>
</c:forEach>

Correctly outputs the intended HTML

What am I doing wrong, should I be using EL to create the string that will populate the "property" attribute of the html:select tag?

UPDATE

The following snippet was also tried and that didn't work either <html:select property="title_guest${gC.index}">

And, neither does this work

<c:set var="guestTitle">title_guest${gC.index}</c:set>
<html:select property="${guestTitle}" styleClass="{required: true}">
 <html:options collection="titles" property="code" labelProperty="value" />
</html:select>

Answer

Salman Paracha picture Salman Paracha · May 13, 2011

After some painful digging around, I seemed to have found the problem and hence the solution. The c:forEach tag does not export the varStatus as a scripting variable and therefore the varStatus variable can not be used in the RT Expr for the property attribute of the html:select tag.

However, the c:forEach does export the varStatus variable as a pageContext attribute, which can be retrieved and used to extract the index/count. The only catch is that you will have to import the javax.servlet.jsp.jstl.core.LoopTagStatus class and use that to manually recreate the varStatus variable so that it can be used inside a scriplet

Here's the snippet of code that worked

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    import="javax.servlet.jsp.jstl.core.LoopTagStatus"
%>
 ...
<c:forEach begin="2" end="${pageView.guestCount}" varStatus="gC">
  <% LoopTagStatus gN = (LoopTagStatus)pageContext.getAttribute("gC"); %>
  <html:select property='<%="title_guest"+gN.getIndex()%>'>
     <html:options collection="titles" property="code" labelProperty="value" />
  </html:select>
</c:forEach>

I don't think this is a clean solution (but could be the only solution). Therefore, I will let the community vote on this answer first (or write a better answer), before I accept it as the final answer.