Page 1 of 2 12 LastLast
Results 1 to 10 of 11

Thread: Bind an empty collection

Hybrid View

  1. #1
    Join Date
    Mar 2005
    Location
    Prague, Czech Republic
    Posts
    34

    Default Bind an empty collection

    Hi,
    I'm using CustomCollectionEditor for binding a multiselect to a collection of domain objects. It works fine except the case when nothing is selected. I'd expect an empty collection, but the collection remains the same as it was set up in formBachingObject().
    I think this happens because there is no request parameter. Is there any common solution? Or should I for example solve this situation in onBing() method.
    Thanks for ideas.

    Here is my binding code:
    Code:
    				<spring&#58;bind path="userForm.user.principal.roles">
    					<select name="$&#123;status.expression&#125;" size="5" multiple="true">
    						<c&#58;forEach var="role" items="$&#123;roles&#125;">
    							<option value="$&#123;role.roleId&#125;"
    								$&#123;gsaf&#58;contains&#40;status.value, role&#41; ? 'selected' &#58; ''&#125;
    							>$&#123;role.name&#125;</option>
    						</c&#58;forEach>
    					</select>
    				</spring&#58;bind>

  2. #2

    Default Any Solution?

    Have you had any luck solving this problem? I am also trying to figure it out.

    John Atwood

  3. #3
    Join Date
    Oct 2005
    Location
    Arlington, VA
    Posts
    14

    Default RE: Bind an empty collection

    Since there is no request parameter passed back when the request is submitted, spring doesn't bind anything and you just end up with the value cached in the session.

    There is a special "marker" field that you can add to the JSP, which is basically the field name that might not get passed back prefixed with an underscore "_". When Spring sees this marker parameter, but no corresponding request parameter, it assumes that the field was not passed and will set the collection to an empty one.

    Below is your JSP example with the hidden "marker" field added.

    Code:
    <spring&#58;bind path="userForm.user.principal.roles">
    
        <input type="hidden" name="_$&#123;status.expression&#125; value="visible"/>
    
        <select name="$&#123;status.expression&#125;" size="5" multiple="true">
          <c&#58;forEach var="role" items="$&#123;roles&#125;">
            <option value="$&#123;role.roleId&#125;"
                        $&#123;gsaf&#58;contains&#40;status.value, role&#41; ? 'selected' &#58; ''&#125;
            >$&#123;role.name&#125;</option>
          </c&#58;forEach>
        </select>
     </spring&#58;bind>

  4. #4
    Join Date
    Nov 2005
    Location
    Valparaiso, Chile
    Posts
    12

    Default

    hi,

    where i can find "gsaf:contains" . Does not appear in my taglibs

  5. #5
    Join Date
    Aug 2004
    Posts
    1,905

  6. #6

    Default fieldMarkerPrefix, What's the point?

    After trying to figure out how to use the fieldMarkerPrefix feature in WebDataBinder I have come to the conclusion that it doesn't do anything usefull. Here is why:

    Consider the case where the data in our MutablePropertyValues mpvs looks like this:

    Code:
    _colors &#91;#ffffff,#fsd234&#93;
    As part of the doBind() method of WebDataBinder checkFieldMarkers() is called. It does exactly what it says it will do.

    Code:
    /**
     * Check the given property values for field markers,
     * i.e. for fields that start with the field marker prefix.
     * <p>The existence of a field marker indicates that the specified
     * field existed in the form. If the property values do not contain
     * a corresponding field value, the field will be considered as empty
     * and will be reset appropriately.
     * @param mpvs the property values to be bound &#40;can be modified&#41;
     * @see #getFieldMarkerPrefix
     * @see #getEmptyValue&#40;String, Class&#41;
     */
    And leaves our mpvs looking like this:

    Code:
    _colors &#91;#ffffff,#fsd234&#93;
    colors   &#91;&#93;
    We go further along with the binding process and end up binding an empty collection to the colors property of the command object, when a collection with two colors should have been bound. Although this is correct according to the javadoc, it does not accomplish the task at hand.

    Furthermore WebDataBinder's checkFieldMarkers() method depends on the getEmptyValue(String field, Class fieldType) method, the default implementation of which returns null if the field type is a Collection. Also rather inconvenient.

    I have solved the problem at hand by subclassing ServletRequestDataBinder. Here is the result:

    Code:
    public class DefaultValueServletRequestDataBinder extends ServletRequestDataBinder
    &#123;
      private PropertyValue&#91;&#93; defaultValues;
      /**
       * Create a new ServletRequestDataBinder instance.
       *
       * @param target     target object to bind onto
       * @param objectName objectName of the target object
       */
      public DefaultValueServletRequestDataBinder&#40;Object target, String objectName&#41;
      &#123;
        super&#40;target, objectName&#41;;
      &#125;
      public void setDefaultValues&#40;PropertyValue&#91;&#93; values&#41;
      &#123;
        this.defaultValues = values;
      &#125;
      protected void doBind&#40;MutablePropertyValues mpvs&#41;
      &#123;
        if&#40;defaultValues != null&#41;
        &#123;
          for&#40;PropertyValue property &#58; defaultValues&#41;
          &#123;
            if&#40;!mpvs.contains&#40;property.getName&#40;&#41;&#41;&#41; mpvs.addPropertyValue&#40;property&#41;;
          &#125;
        &#125;
        super.doBind&#40;mpvs&#41;;
      &#125;
    &#125;
    Although this approach requires changing your Controller classes slightly to use the new DefaultValueServletRequestDataBinder, it works, and in my opinion is more convenient.

  7. #7
    Join Date
    Oct 2005
    Posts
    3

    Default

    Hi,

    I need to do binding on collection, just like what you guys been discussing here. However, I'm a little bit lost cause i'm kinda new to Spring. Would anybody be kindly enough to tell me step by step on how to do it?

    regards,
    gondezz

  8. #8

    Default Simple example of binding to a collection

    Here is your command bean:

    Code:
    public class MyCommand
    &#123;
      private Set<Integer> numbers;
      public void setIntegers&#40;Set<Integer> numbers&#41;
      &#123;
        this.numbers = numbers;
      &#125;
      public void Set<Integer> getNumbers&#40;&#41;
      &#123;
        return numbers;
      &#125;
    &#125;
    Here is your jsp:

    Code:
    <form>
      <spring&#58;bind name="command.numbers">
        <input type="checkbox" value="1" name="$&#123;status.value&#125;"/>
        <input type="checkbox" value="2" name="$&#123;status.value&#125;"/>
        <input type="checkbox" value="3" name="$&#123;status.value&#125;"/>
        <input type="checkbox" value="4" name="$&#123;status.value&#125;"/>
      </spring&#58;bind>
      <input type="submit"/>
    </form>
    When the user clicks the submit button in their browser, the values of the selected checkboxes will be submitted as a comma delimenated string. For example if the user checks the first two check boxes, the resulting HTTPServletRequest will contain the following parameter:

    Code:
    numbers = 1,2
    You will need to provide your ServletRequestDataBinder with a PropertyEditor that knows how to convert the provided input into Set<Integer>. It inititialize your ServletRequestDataBinder override your controller's initBinder(ServletRequestDataBinder binder, HttpServletRequest request) method;

    For example:

    Code:
    protected void initBinder&#40;HttpServletRequest request, ServletRequestDataBinder binder&#41; throws Exception
      &#123;
        binder.registerCustomEditor&#40;Set.class,"numbers",new NumbersSetEditor&#40;&#41;&#41;;
    &#125;
    You have to write NumbersSetEditor yourself. It should extend PropertyEditorSupport. Look at some of the PropertyEditors already included with spring for an example. For example CustomDateEditor is a good one. You should also strongly consider extending CustomCollectionEditor, as you will have better code reuse.

    I hope this is enough of an example to get you started. Please bear in mind that I typed most of this code in from memory, so it may not compile. Good luck.

  9. #9
    Join Date
    Oct 2005
    Posts
    17

    Default

    I know that is more work but could you not solve the problem using javascript.
    By setting the list of values into a hidden field, have javascript populate the select and then onSubmit() look at the selected values and update the hidden field.
    it might even be enough to populate the hidden field only if no values selected.

    it is a bit of a hack though, just put it there in case it helps
    Yann

    Reaching Zen a day at a time, or not...

  10. #10

    Default

    Having just solved this problem myself, it appears to me as though JohnDoeKyrgyz assessment of fieldMarkerPrefix isn't valid. I believe he misunderstood the use of it.

    It should be used in this fashion:

    Code:
    <select multiple="multiple" name="${spring.status.expression}">
    ... options
    </select>
    	
    <input type="hidden" name="_${spring.status.expression}" value="emptySet"/>
    Now when WebDataBinder.checkFieldMarkers() is called, if only _${spring.status.expression} exits, then it calls WebDataBinder.getEmptyValue() and puts that name/value in the mpvs. If both _${spring.status.expression} and ${spring.status.expression} are in the mvps, it disregards the _${spring.status.expression}.

    The default return value for WebDataBinder.getEmptyValue() for a Set is null. This does effectively "empty out" the set, which was the initial problem, but for me it lead to null pointer exceptions in other parts of my code. What I really wanted was an empty set, so I decided to extend the ServletRequestDataBinder and over-ride getEmptyValue(). My solution looks a little like this:

    Code:
    public class DefaultValueServletRequestDataBinder extends ServletRequestDataBinder {
    
    	  public DefaultValueServletRequestDataBinder(Object target, String objectName)
    	  {
    	    super(target, objectName);
    	  }
    	  
    	  protected Object getEmptyValue(String field, Class fieldType) {
    		  if (fieldType != null && fieldType.equals(Set.class)) { 
    			  // Special handling for sets
    			  return new HashSet();
    		  }
    		  else
    			  return super.getEmptyValue(field, fieldType);
    	  }	  
    }
    After doing this I had to over-ride Spring's BaseCommandController.createBinder() in order to use my new DefaultValueServletRequestDataBinder.

    I hope this is helpful.

Similar Threads

  1. Replies: 0
    Last Post: Jul 25th, 2005, 08:48 AM
  2. how do I bind a collection
    By krizvi in forum Web
    Replies: 1
    Last Post: Mar 1st, 2005, 01:48 PM
  3. Replies: 13
    Last Post: Dec 7th, 2004, 10:00 AM
  4. How to bind objects of a collection?
    By jboring in forum Web
    Replies: 1
    Last Post: Oct 6th, 2004, 01:26 AM
  5. Replies: 8
    Last Post: Sep 23rd, 2004, 01:12 AM

Posting Permissions

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