SPR-2733 Improvement for handling checkboxes in web forms (patch included)
Introduced default field prefix of '!', which can be overridden with WebDataBinder#setFieldDefaultPrefix. If a field is otherwise not present, the default value is used for the field. Field markers for the same field are ignored.
This commit is contained in:
parent
8e261e5833
commit
61b5428211
|
|
@ -34,14 +34,17 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
* HTML checkboxes and select options: detecting that a field was part of
|
||||
* the form, but did not generate a request parameter because it was empty.
|
||||
* A field marker allows to detect that state and reset the corresponding
|
||||
* bean property accordingly.
|
||||
* bean property accordingly. Default values, for parameters that are otherwise
|
||||
* not present, can specify a value for the field other then empty.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Scott Andrews
|
||||
* @since 1.2
|
||||
* @see #registerCustomEditor
|
||||
* @see #setAllowedFields
|
||||
* @see #setRequiredFields
|
||||
* @see #setFieldMarkerPrefix
|
||||
* @see #setFieldDefaultPrefix
|
||||
* @see ServletRequestDataBinder
|
||||
*/
|
||||
public class WebDataBinder extends DataBinder {
|
||||
|
|
@ -58,9 +61,19 @@ public class WebDataBinder extends DataBinder {
|
|||
*/
|
||||
public static final String DEFAULT_FIELD_MARKER_PREFIX = "_";
|
||||
|
||||
/**
|
||||
* Default prefix that field default parameters start with, followed by the field
|
||||
* name: e.g. "!subscribeToNewsletter" for a field "subscribeToNewsletter".
|
||||
* <p>Default parameters differ from field markers in that they provide a default
|
||||
* value instead of an empty value.
|
||||
* @see #setFieldDefaultPrefix
|
||||
*/
|
||||
public static final String DEFAULT_FIELD_DEFAULT_PREFIX = "!";
|
||||
|
||||
private String fieldMarkerPrefix = DEFAULT_FIELD_MARKER_PREFIX;
|
||||
|
||||
private String fieldDefaultPrefix = DEFAULT_FIELD_DEFAULT_PREFIX;
|
||||
|
||||
private boolean bindEmptyMultipartFiles = true;
|
||||
|
||||
|
||||
|
|
@ -118,6 +131,32 @@ public class WebDataBinder extends DataBinder {
|
|||
return this.fieldMarkerPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a prefix that can be used for parameters that indicate default
|
||||
* value fields, having "prefix + field" as name. The value of the default
|
||||
* field is used when the field is not provided.
|
||||
* <p>Default is "!", for "!FIELD" parameters (e.g. "!subscribeToNewsletter").
|
||||
* Set this to null if you want to turn off the field defaults completely.
|
||||
* <p>HTML checkboxes only send a value when they're checked, so it is not
|
||||
* possible to detect that a formerly checked box has just been unchecked,
|
||||
* at least not with standard HTML means. A default field is especially
|
||||
* useful when a checkbox represents a non-boolean value.
|
||||
* <p>The presence of a default parameter preempts the behavior of a field
|
||||
* marker for the given field.
|
||||
* @see #DEFAULT_FIELD_DEFAULT_PREFIX
|
||||
* @see org.springframework.web.servlet.mvc.BaseCommandController#onBind
|
||||
*/
|
||||
public void setFieldDefaultPrefix(String fieldDefaultPrefix) {
|
||||
this.fieldDefaultPrefix = fieldDefaultPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the prefix for parameters that mark default fields.
|
||||
*/
|
||||
public String getFieldDefaultPrefix() {
|
||||
return this.fieldDefaultPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to bind empty MultipartFile parameters. Default is "true".
|
||||
* <p>Turn this off if you want to keep an already bound MultipartFile
|
||||
|
|
@ -139,16 +178,42 @@ public class WebDataBinder extends DataBinder {
|
|||
|
||||
|
||||
/**
|
||||
* This implementation performs a field marker check
|
||||
* This implementation performs a field default and marker check
|
||||
* before delegating to the superclass binding process.
|
||||
* @see #checkFieldDefaults
|
||||
* @see #checkFieldMarkers
|
||||
*/
|
||||
@Override
|
||||
protected void doBind(MutablePropertyValues mpvs) {
|
||||
checkFieldDefaults(mpvs);
|
||||
checkFieldMarkers(mpvs);
|
||||
super.doBind(mpvs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the given property values for field defaults,
|
||||
* i.e. for fields that start with the field default prefix.
|
||||
* <p>The existence of a field defaults indicates that the specified
|
||||
* value should be used if the field is otherwise not present.
|
||||
* @param mpvs the property values to be bound (can be modified)
|
||||
* @see #getFieldDefaultPrefix
|
||||
*/
|
||||
protected void checkFieldDefaults(MutablePropertyValues mpvs) {
|
||||
if (getFieldDefaultPrefix() != null) {
|
||||
String fieldDefaultPrefix = getFieldDefaultPrefix();
|
||||
PropertyValue[] pvArray = mpvs.getPropertyValues();
|
||||
for (PropertyValue pv : pvArray) {
|
||||
if (pv.getName().startsWith(fieldDefaultPrefix)) {
|
||||
String field = pv.getName().substring(fieldDefaultPrefix.length());
|
||||
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
|
||||
mpvs.addPropertyValue(field, pv.getValue());
|
||||
}
|
||||
mpvs.removePropertyValue(pv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the given property values for field markers,
|
||||
* i.e. for fields that start with the field marker prefix.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
|||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Scott Andrews
|
||||
*/
|
||||
public class ServletRequestDataBinderTests {
|
||||
|
||||
|
|
@ -90,6 +91,59 @@ public class ServletRequestDataBinderTests {
|
|||
assertFalse(target.isPostProcessed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldDefault() throws Exception {
|
||||
TestBean target = new TestBean();
|
||||
ServletRequestDataBinder binder = new ServletRequestDataBinder(target);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addParameter("!postProcessed", "off");
|
||||
request.addParameter("postProcessed", "on");
|
||||
binder.bind(request);
|
||||
assertTrue(target.isPostProcessed());
|
||||
|
||||
request.removeParameter("postProcessed");
|
||||
binder.bind(request);
|
||||
assertFalse(target.isPostProcessed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldDefaultPreemptsFieldMarker() throws Exception {
|
||||
TestBean target = new TestBean();
|
||||
ServletRequestDataBinder binder = new ServletRequestDataBinder(target);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addParameter("!postProcessed", "on");
|
||||
request.addParameter("_postProcessed", "visible");
|
||||
request.addParameter("postProcessed", "on");
|
||||
binder.bind(request);
|
||||
assertTrue(target.isPostProcessed());
|
||||
|
||||
request.removeParameter("postProcessed");
|
||||
binder.bind(request);
|
||||
assertTrue(target.isPostProcessed());
|
||||
|
||||
request.removeParameter("!postProcessed");
|
||||
binder.bind(request);
|
||||
assertFalse(target.isPostProcessed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldDefaultNonBoolean() throws Exception {
|
||||
TestBean target = new TestBean();
|
||||
ServletRequestDataBinder binder = new ServletRequestDataBinder(target);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addParameter("!name", "anonymous");
|
||||
request.addParameter("name", "Scott");
|
||||
binder.bind(request);
|
||||
assertEquals("Scott", target.getName());
|
||||
|
||||
request.removeParameter("name");
|
||||
binder.bind(request);
|
||||
assertEquals("anonymous", target.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithCommaSeparatedStringArray() throws Exception {
|
||||
TestBean target = new TestBean();
|
||||
|
|
|
|||
Loading…
Reference in New Issue