Announcement Announcement Module
Collapse
No announcement yet.
binding hashmaps with <spring:bind> Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • binding hashmaps with <spring:bind>

    I have been searching for this all along , but could not find a proper one. So calling for help

    This is my jsp:

    HTML Code:
      <form:form name="mu_mgmt" commandName="muFetch" method="post">
    	   <table border="1" width="400" cellpadding="2" cellspacing="2">
    	   <tr>
    	      <td bgcolor="#CCCCCC" colspan="2" align="center" valign="top">
    	      Properties for Manufacturing Line id : ${muIdForView.muId}  
              </td>
    	   </tr>
     	   <core:forEach var="mup" items="${muFetch}"> 
            <tr>
    		  <td align="right">
    		     <core:out value="${mup.key}"/>
    		  </td>
    		  <td align="left">
    		  <spring:bind path="$[<???>]">
    		  <input type="text" name = "<core:out value="${mup.key}"/>" 
    		                              value="<core:out value="${mup.value}"/>"> 
    		  </spring:bind>
    		  </td>
      	    </tr>
    		</core:forEach>
    	  </table>
         <br>
         <br>
         <center><input type="submit" value="Add To Production Line"></center>
    	 </form:form>   
    	   	 
    I would like to bind the value from the text box to the hashmap , that is being iterated using <c:forEach>. I am able to correctly display the keys and their values. Can someone pls help with this. Kind request not to refer to links as most of the stuff that i have been looking for doesnt suit my need.

    Regards,
    Kartik

  • #2
    Fundamentally, the question boils down to, "How can one display and process dynamic forms?"

    I spent hours on this problem- not just because I'm a nice guy- but because I realized that a similar use case is going to pop up on me soon. I have a Struts/Spring application that uses a service to describe questions that the user must be asked (and a question has multiple fields). There are literally thousands of questions a user might be asked and there is certainly not one Action and JSP for each question; rather there is one generic Action and one generic JSP that basically form a framework for handling any question. I need to rewrite this using Spring MVC/Spring, so it's too my benefit to really nail this down (and help out my fellow Spring user in the process ).

    So, here's the example I cooked up. It's pretty dumb, but I think you could quite easily adapt it to what you're doing. Note that I used Spring 2.0 and the spring:bind tag has fallen out of favor. I opted for form:input instead. If you need to use Spring 1.2.x, you can adapt the example to use spring:bind quite easily.

    Also, I do not want to give the impression that this is the "correct" way of doing this, but I had as much trouble as you finding a good example of this sort of thing and I feel the final result is very clean and therefore quite acceptable.

    Here goes...

    First the domain object (which is reused as a command object):

    Code:
    public class Example {
    
        private Map myMap = new HashMap();
        
        public Map getMyMap() {
            return myMap;
        }
        
        public void setMyMap(Map myMap) {
            this.myMap = myMap;
        }
        
    }
    Now the controller:

    Code:
    public class ExampleController extends AbstractFormController {
        
    	protected ModelAndView showForm(HttpServletRequest request, HttpServletResponse response, BindException bindException) throws Exception {
    	    ModelAndView mav = new ModelAndView();
    	    // In real life, you'd build this data with a service of some sort
                Map myMap = new HashMap();
    	    myMap.put("Captain", "Kirk");
    	    myMap.put("Science Officer", "Spock");
    	    myMap.put("Doctor", "McCoy");
    	    Example example = new Example();
    	    example.setMyMap(myMap);
    	    mav.addObject("example", example);
    	    mav.setViewName("example");
    	    return mav;
    	}
    	
        protected ModelAndView processFormSubmission(HttpServletRequest request, HttpServletResponse response, Object command, BindException bindException) throws Exception {
            ModelAndView mav = new ModelAndView();
            mav.addObject("example", command);
            // Note: I'm lazy, so we're just going to go back to the same view
    	mav.setViewName("example");
            return mav;
        }
    
    }
    And finally the JSP. Some things to look out for in here: the iteration over the items in the Map is more or less disjoint from what goes on with the form binding tags. I do it pretty much just to get row replication and to get the keys for each element of the map because they're important for constructing the paths (and providing labels).

    Code:
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
    
    <form:form method="POST" commandName="example">
    	<table>
    		<c:forEach items="${example.myMap}" var="myItem">
    			<tr>
    				<td><form:label path="myMap['${myItem.key}']">${myItem.key}</form:label></td>
    				<td><form:input path="myMap['${myItem.key}']"/></td>
    			</tr>
    		</c:forEach>
    		<tr>
    			<td>
    				<input type="submit"/>
    			</td>
    		</tr>
    	</table>
    </form:form>
    I hope this helps you a bit. If any of this isn't clear or it doesn't work for some reason, feel free to post a follow-up. As I stated, I have significant interest in this use case.

    Cheers.

    Comment


    • #3
      The input has to be bound to the Map.Entry's value, hasn't it?

      Code:
      <form:input path="myMap['${myItem.value}']"/>
      Joerg

      Comment


      • #4
        I think there is a 'feature' with map binding. If i'm not mistaken, the bind key is the .toString() value of the key. this works if the key value is a string. If the key is an object, binding fails unless you implement .toString and force .equals .hashCode to work on the .toString value. This isn't a very extensible model as I'm forced to implement toString for a non-debug purpose.

        Comment


        • #5
          Originally posted by Jörg Heinicke View Post
          The input has to be bound to the Map.Entry's value, hasn't it?

          Code:
          <form:input path="myMap['${myItem.value}']"/>
          Joerg
          Jorg,

          That wouldn't work. It's basically telling the binder to do myMap.put("<old value name here>", "<new value name here>");

          With the key taken out of the equation, it doesn't work. I did get my solution described in my first post to work before posting it and I verified that binding does take place (and correctly) prior to the invocation of processFormSubmission.

          Comment


          • #6
            Originally posted by jonnio View Post
            I think there is a 'feature' with map binding. If i'm not mistaken, the bind key is the .toString() value of the key. this works if the key value is a string. If the key is an object, binding fails unless you implement .toString and force .equals .hashCode to work on the .toString value. This isn't a very extensible model as I'm forced to implement toString for a non-debug purpose.
            Yes, I noticed that as well. My perception was that it only works with String as keys. Interesting that you went ahead and thought about the exact conditions to make it work with objects. Synchronizing equals() and hashCode() with toString() is really a pain ...

            Originally posted by krancour View Post
            That wouldn't work. It's basically telling the binder to do myMap.put("<old value name here>", "<new value name here>");
            Ehm, first this binding is about the direction from form to object. That one is purely based on form input names and so request parameter names.

            Second, you are right though I was confused by the double usage of the key, but the one is ${myItem.key} and so refers to the actual key. The other one is myMap['${myItem.key}'], which eventually means map.get(myItem.getKey()) and so refers to the actual value.

            In theory it would be possible to use ${myItem.value} since it refers to the same object. But to get back to the correct request parameter names: This would break the path and Spring would not find the object and could not create a correct field input name.

            Joerg

            Comment


            • #7
              Thanks for the info

              Thanks a lot for the info guys. Got my problem sorted out.

              Comment

              Working...
              X