Results 1 to 7 of 7

Thread: binding hashmaps with <spring:bind>

  1. #1
    Join Date
    Oct 2007
    Posts
    23

    Default 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. #2
    Join Date
    Sep 2006
    Location
    Hartford, CT
    Posts
    145

    Default

    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.

  3. #3
    Join Date
    Nov 2005
    Location
    Reutlingen, Germany
    Posts
    2,098

    Default

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

    Code:
    <form:input path="myMap['${myItem.value}']"/>
    Joerg
    This post can contain insufficient information.

  4. #4

    Default

    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.

  5. #5
    Join Date
    Sep 2006
    Location
    Hartford, CT
    Posts
    145

    Default

    Quote 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.

  6. #6
    Join Date
    Nov 2005
    Location
    Reutlingen, Germany
    Posts
    2,098

    Default

    Quote 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 ...

    Quote 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
    This post can contain insufficient information.

  7. #7
    Join Date
    Oct 2007
    Posts
    23

    Default Thanks for the info

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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •