Page 2 of 2 FirstFirst 12
Results 11 to 17 of 17

Thread: Binding composite properties with DataBinder

  1. #11
    Join Date
    Feb 2007
    Posts
    21

    Thumbs up Binding several input fields straight into a java.util.Date

    Dear All,

    I want to share with you my findings on the topic of binding data originating from different HTML inputs straight into a java.util.Date.

    It does not require any javascript, any new PropertyEditor, anything... Hopefully you will also like it!!!!

    Let's consider an example. Your HTML view has:
    • an input for the day where you expect the user to enter DD/MM/YYYY
    • a drop down for selecting hours
    • a drop down for selecting minutes


    Your form object has a java.util.Date attribute called travelDate.

    The binding
    1. By naming all the HTML elements after the attribute name: travelDate
    2. Register a CustomDateEditor in your Controller (binder.registerCustomEditor) with a comma separated list of patterns that match in order the appearance of your HTML input fields. This is the trick!!


    Spring will invoke the setAsText of your CustomDateEditor by passing the user input in the form of comma separated string. For instance '07/02/2007,14,37'.

    This will in turn be parsed as a date.

  2. #12
    Join Date
    Feb 2007
    Location
    Moscow, Russia
    Posts
    56

    Default

    Quote Originally Posted by dantelope View Post
    Instead, I've created a form object which contains a domain object and a helper bean consisting of the month, day, and year and a date-conversion method that either returns a date or, if the date is invalid, null.
    Yeah, that's similar to what we did in our latest project.
    We've created a holder object, binded its properties, e.g. dateField.day, dateField.month, dateField.year and placed date conversion/validation code into
    the holder. Note that even if all three fields are valid, the resulting object may not represent a valid date.

    It does not require you to construct any custom editors. Custom editors also do not have access to Errors, so validators are better here.

  3. #13

    Default

    Adelinor,

    your solution seems like a nice, straightforward approach... if there wouldn't be bind / validation errors.

    Let's go with the initial example of having three input fields; day, month, year. These should be bound to a java.util.Date using your technique. How do you manage to refill the form fields in case of bind or validation errors? The getAsText always returns all values in a comma seperated string (e.g. 09,02,1941). Your form fields end up showing the following values:
    day = 09
    month = 09
    year = 09,0

    Am I missing something here? Is there a way to solve this issue without writing custom code for it and simply using the CustomeDateEditor as you suggested?

  4. #14
    Join Date
    Feb 2007
    Posts
    21

    Post Display of bind errors

    Indeed this where you need to write some unelelgant code in the JSP...

    Using the spring:bind custom tag, you get back an array of input strings. Using JSTL, you can go through this array and set variable for each individual member of the date.

    Considering the example of three inputs for day, month and year, you would get the individual values like that:

    Code:
    <spring:bind path="form.birthDate">
        <c:forEarch items="${status.value}" var="item" varStatus="loopStatus">
            <c:choose>
                <c:when test="${loopStatus == 0}">
                    <c:set var="day" value="${item}"/>
                </c:when>
                <c:when test="${loopStatus == 1}">
                    <c:set var="month" value="${item}"/>
                </c:when>
                <c:when test="${loopStatus == 2}">
                    <c:set var="year" value="${item}"/>
                </c:when>
            </c:choose>
        </c:forEach>
    </spring:bind>
    After the previous code (that could be included in a JSP include or tile), the individual date field values are available for display:

    Code:
    <input name="birthDate" value="<c:out value="${day}"/>">
    <input name="birthDate" value="<c:out value="${month}"/>">
    ...
    -- adelino

  5. #15

    Default

    Thanks for the feedback. This, however, doesn't look as straightforward anymore Especially not if you have a lot of pages where you need dates like this. Don't really like the bulky code in the JSPs for such a simple task.

    Besides that it might be kinda tricky to implement it when you want to use Spring 2.0 form tags, won't it?

    Although I haven't tried it for this scenario I think I prefer the approach with the holder object mentioned above.

  6. #16
    Join Date
    Apr 2007
    Location
    Sydney, Australia
    Posts
    40

    Default as an experiment out of interest

    after some investigation, i would also conclude as with others that the wrapping object would be the most straightforward implementation, especially if you are using spring 2 form tags.

    However, there is something so deceptively attractive about adelinor's method to take advantage of spring automatic comma seperated binding, and there is a way to implement the view in a single line, using the function tag library. (only applies to spring bind tags)

    The key is this: fn: split(status.value, '/')[0]

    When getAsText() of your CustomDateEditor is called, it will return the formatted string, which you can split out.

    Example code:
    Code:
    <spring:bind path="command.dob">
    	<select name="${status.expression }" id="day">
    		<c:forEach items="${dayMap}" var="elem">
    			<option value="${elem.key}" <c:if test="${fn:split(status.value, '/')[0] eq elem.key}" >selected="selected"</c:if>>${elem.value }</option>
    		</c:forEach>		
    	</select>
    	<select name="${status.expression }" id="month">
    		<c:forEach items="${monthMap}" var="elem">
    			<option value="${elem.key}" <c:if test="${fn:split(status.value, '/')[1] eq elem.key}" >selected="selected"</c:if>>${elem.value }</option>
    		</c:forEach>				
    	</select>
    	<select name="${status.expression }" id="year">
    		<c:forEach items="${yearMap}" var="elem">
    			<option value="${elem.key}" <c:if test="${fn:split(status.value, '/')[2] eq elem.key}" >selected="selected"</c:if>>${elem.value }</option>
    		</c:forEach>
    	</select>
    </spring:bind>
    For completeness, here is the java code:

    Code:
    class CompositeCustomDateEditor extends CustomDateEditor{
        public CompositeCustomDateEditor(DateFormat dateFormat, boolean allowEmpty) {
            super(dateFormat, allowEmpty);
        }
        public void setAsText(String text) {
            if (text != null) {
              super.setAsText(text.replace(',', '/'));
            }
        }
    }
    
    public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
    
        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
        binder.registerCustomEditor( Date.class, "dob", new CompositeCustomDateEditor(dateFormat, true));		
    }
    You will notice that these dropdowns do not have the ability to be blank. This is because it would lead to a bug where if day was not selected, and month and year were selected, after submit, the custom editor cannot set a valid date, hence you would lose the values you selected for month and year. I don't think there is a way around this. As nekoval pointed out, there is no binding error support in custom editors.

    If you are binding composite string fields though, you can hack around this, see my next post below.

  7. #17
    Join Date
    Apr 2007
    Location
    Sydney, Australia
    Posts
    40

    Default do the following at your own peril

    As I stated above, this is not the best way to do it, but If you are binding composite String fields (e.g. phone number field, seperated into area code and phone number), then this method could be usable.

    Once again, applies to spring: bind tags only.

    java code:
    Code:
    /*	 custom editor that is able to join 2 composite phone number fields with a "-" */ 
    class CustomPhoneNumberEditor extends PropertyEditorSupport {
    		public CustomPhoneNumberEditor() {}
    		public void setAsText(String text) {
    			if (text != null) {
    				if (",".equals(text))
    					setValue(null);    // both fields were empty
    				else
    					setValue(text.replace(',', '-'));
    			}
    		}
    		public String getAsText() {
    			//so that textbox wont display "null"
    			if (getValue()==null)
    			    return null; 
    			
    			String result = (String)getValue();
    			
    			// fn:split doesn't support empty tokens
    			// this allows values to display properly when nothing was entered
    			if (result.startsWith("-")) 
    				result = " " + result;  
    			if (result.endsWith("-")) 
    				result = result + " ";
    			return result;
    		}
    	};
    	
    }
    public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
       binder.registerCustomEditor(String.class, "phoneNumber", new CustomPhoneNumberEditor());		
    }
    JSP code:

    Code:
    <spring:bind path="command.phoneNumber">
    	<input type="text" value="${fn:trim(fn:split(status.value, '-')[0])}" name="${status.expression }" />
    	<input type="text" value="${fn:trim(fn:split(status.value, '-')[1])}" name="${status.expression }" />
    </spring:bind>
    Following on from my previous post, this code solves the problem when you dont enter an area code, and you enter a phone number. This scenario will post ",91234567" which binds to "-91234567". When it returns to the JSP and you do a split, you won't have 2 elements in the list (since split doesn't support null tokens).

    The magic of the hack lies in manually detecting and changing your null tokens to a space " " , then using fn:trim to get rid of it again.

    So in conclusion, this was quite elegent in theory, but ended up looking like a big hack. I'd go with the wrapping object...

    Hope that may have been helpful to someone.

Similar Threads

  1. FlowExecutionStorage in a DB
    By cacho in forum Web Flow
    Replies: 7
    Last Post: Oct 19th, 2009, 03:36 PM
  2. Replies: 2
    Last Post: May 9th, 2005, 06:25 PM
  3. Replies: 2
    Last Post: Jan 18th, 2005, 04:06 AM
  4. Binding to map properties
    By scroyston in forum Swing
    Replies: 1
    Last Post: Dec 3rd, 2004, 05:44 PM
  5. Replies: 2
    Last Post: Aug 17th, 2004, 04:16 PM

Posting Permissions

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