Announcement Announcement Module
Collapse
No announcement yet.
<form:input> inside <c:forEach> Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • <form:input> inside <c:forEach>

    I'm new to Spring MVC 2.5 (coming from Struts) and I'm trying to figure out the simplest, best practice for setting up a form that saves to a collection. For example, I would like to display an editable list of products. Here is how I *wish* it worked:

    Code:
    <form:form method="post" commandName="allProductEdit">
       <c:forEach items="${allProductEdit.products}" var="product">
          <form:input path="product.description" />
          <form:input path="product.price" />
       </c:forEach>
       <input type="submit" value="Execute">
    </form:form>
    Basically, I want the paths that are passed to the form:inputs to be relative to the c:forEach variable "product". The products object is a List, by the way.

    To make the JSP work, I had to do this instead, which just seems wrong:

    Code:
    <form:form method="post" commandName="allProductEdit">
       <c:forEach items="${allProductEdit.products}" var="prod">
          <form:input path="products[${prod.id}].description" />
          <form:input path="products[${prod.id}].price" />
       </c:forEach>
       <input type="submit" value="Execute">
    </form:form>
    Certainly there is a best-practice, intuitive way to do this! Could anyone point me in the right direction?

    Thanks,
    Ryan

  • #2
    Well actually neither is correct. You need to include the index inside the indexed collection (the list in your case). You need to include a varStatus attribute and use that, instead of your product.

    Code:
    <form:form method="post" commandName="allProductEdit">
       <c:forEach items="${allProductEdit.products}" var="prod" varStatus="pStatus">
          <form:input path="products[${pStatus.index}].description" />
          <form:input path="products[${pStatus.index}].price" />
       </c:forEach>
       <input type="submit" value="Execute">
    </form:form>
    Your form:input tag doesn't and shouldn't know anything about the fact that it is used inside another tag. That is why you need to include the index.

    Comment


    • #3
      Marten,

      That works. Thank you for your help.

      Ryan

      Comment


      • #4
        Hi,

        I am facing a similar problem:
        I want to use a bulk edit for serveral items of the same kind, but my view always throws an error ...

        Let's have a look at the coding:

        I retrieve a list from my service interface:
        Code:
        public List<ProjectWrapper> getCollaboratingProjectsByProjectId( Integer id );
        This method returns the list which is passed to the jsp page in the @Controller.

        Code:
        @Controller
        @RequestMapping( "/projectCollaboration.html" )
        public class ProjectCollaborationController {
        
          private IProjectCollaborationAssignmentManager projectCollaborationManager;
        
          public static final String COLLABORATIONVIEW = "projectCollaboration";
        
          @RequestMapping( method = RequestMethod.GET )
          public String getProjectCollaborationListing( ModelMap model,
              @RequestParam ( "projectId" ) Integer projectId ) {
            
            model.addAttribute( "projects", projectCollaborationManager.getCollaboratingProjectsByProjectId( projectId ) );
            model.addAttribute( "projectId", projectId );
            
            return COLLABORATIONVIEW;
          }
        
          // Setter and Getter
        }
        Now I want to iterate the list and be able to make a bulk edit.

        The jsp looks like this:

        Code:
        <form:form >
          
          <table>
            <c:forEach items="${projects}" var="project" varStatus="rowItem" >
            <tr>
              <td>
                <form:input path="projects[${rowItem.index}].name" />
              </td>
            </tr>
            </c:forEach>
            <tr>
              <td colspan="3" style="text-align: right;">
                <input type="submit" value="Update" />
              </td>
            </tr>
          </table>
        </form:form>
        The line: <form:input path="projects[${rowItem.index}].name" /> is throwing the following error:

        Code:
        org.apache.jasper.JasperException: An exception occurred processing JSP page /WEB-INF/jsp/projectCollaboration.jsp at line 25
        
        22:       <td>
        23:         
        24:         
        25:         <form:input path="projects[${rowItem.index}].name" />
        26:       </td>
        27:       <td>
        28:         <c:out value="${project.name}" />
        
        
        Stacktrace:
        	org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:524)
        	org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:429)
        	org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
        	org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
        	javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
        	org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:240)
        	org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:258)
        ...	
         
          root cause
        
        java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
        	org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
        	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:172)
        	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:192)
        	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:158)
        	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:145)
        	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:136)
        	org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:120)
        	org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:379)
        	org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:139)
        	org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:90)
        
        ...
        My project wrapper looks like this:
        Code:
        public class ProjectWrapper implements IProjectWrapper, Serializable {
          private IProject project;
          private boolean isEnabled = false;
          
          public ProjectWrapper() {}
          
          public ProjectWrapper( IProject project, boolean isEnabled ) {
            
            this.project = project;
            this.isEnabled = isEnabled;
          }
           
          public IProject getProject() {
            return project;
          }
        
          public void setProject( IProject project ) {
            this.project = project;
          }
        
          public boolean isEnabled() {
            return isEnabled;
          }
        
          public void setEnabled( boolean isEnabled ) {
            this.isEnabled = isEnabled;
          }
            
          // Wrapped information
          public String getName() {
            return project.getName();
          }
          
          public Integer getId() {
            return project.getId();
          }
          
          public String getDescription() {
            return project.getDescription();
          }
        }
        The ${rowItem.index} itself is working well, but cannot figure out where the problem is. All approaches, I had a look at, look very similar..

        Is there a problem with annotated controllers and the command object? What am I missing?

        Please help me! Thx in advance!

        PS: If any information is missing, please ask

        Comment


        • fineriff
          fineriff commented
          Editing a comment
          hi, i have the similar problem,you somehow solve the problem?
      Working...
      X