Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring checkbox JSP Tag is broken when using Converter for type Boolean

I've set up a CRUD web application with Spring Roo and Spring MVC. My problem is: since I'm using a converter for localizing the displaying of boolean values the Spring JSP Tag checkbox is broken which means the checkboxes don't take the real value from the backing beans. They are always false and unchecked.

I've done some research and probably found the error in the writeTagDetails method of org.springframework.web.servlet.tags.form.CheckboxTag. Here is the interesting part of this method:

// the concrete type may not be a Boolean - can be String
if (boundValue instanceof String) {
    boundValue = Boolean.valueOf((String) boundValue);
}
Boolean booleanValue = (boundValue != null ? (Boolean) boundValue : Boolean.FALSE);
renderFromBoolean(booleanValue, tagWriter);

Because I'm using a converter to display yes/no instead of true/false the boundValue is a String and the call of Boolean.valueOf always results in false because the valueOf method isn't aware of the used Spring Converter and interprets yes/no as false.

How can I solve this issue with Spring? Has anybody a clue? My brain has reached a blind alley.

Just for completeness: The converter for the Boolean type is working as expected (code see below).

public class BooleanConverter implements Converter<Boolean,String>, Formatter<Boolean> {

@Autowired
private MessageSource messageSource;

@Override
public String print(Boolean object, Locale locale) {
    return (object)
            ? messageSource.getMessage("label_true", null, LocaleContextHolder.getLocale())
            : messageSource.getMessage("label_false", null, LocaleContextHolder.getLocale());
}

@Override
public String convert(Boolean source) {
    return this.print(source, null);
}
}
like image 969
Felix Avatar asked Jul 04 '11 19:07

Felix


3 Answers

This seems possible to overcome. That is you want a human readable Formatter to display yes/no to the user for boolean values in the model. But you still want the checkbox HTML elements to work and it appears those HTML checkbox elements/widgets/JSP tags expects true/false string to be used (or a boolean Java type) it doesn't appear to use the converter to get the arbitrary yes/no string back to a boolean type.

This problem for me manifests itself as the initial state of the checkbox is never ticked when the model has a Boolean.TRUE value set. This means any read-modify-update of the record (without editing that field ends up transitioning from 'true' to 'false' when it was not changed by the user). This is due to the initial state in the UI being inconsistent with the model (it shows always unchecked i.e. false state) even when model is true state. The displayed value is of an unchecked checkbox in the HTML edit record screen, even when the model has Boolean.TRUE for that value. This is because "yes" does not get interpreted as "true" by the HTML checkbox elements and it defaults to false (as that is the default boolean value).

So define your Formatter/Converter (as you are already doing). But in your @Controller add:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Boolean.class, "friesWithThat", new CustomBooleanEditor(false));
}

This appears to make the string display value continue to be yes/no but the value used and passed to the checkbox HTML elements continues to be true/false.

Now when editing/updating the record (in CRUD) the initial state of the checkbox is consistent with the model and saving the data (without editing any fields) does not transition the checkbox state (which is my understanding of the problem you have).

So from this I think we can understand that Converters/Formatters are for general display of data and that PropertyEditors are for mapping model data so the data needed by the UI widget.

like image 190
Darryl Miles Avatar answered Oct 26 '22 23:10

Darryl Miles


Probably you should write your own type called Choice which has Choice.YES and Choice.NO as ordinal enumerations which correspond to 1 or 0 based on the values stored in the database.

Then you can have your own display tags and input tags defined within the application for this type which would address this issue.

like image 30
bhagyas Avatar answered Oct 27 '22 00:10

bhagyas


Adding to the previous answer, from spring 3.2 you can register the property editor for all controllers and all Boolean fields like this:

package your.package.path;

import org.springframework.beans.propertyeditors.CustomBooleanEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalBindingInitializer {

    @InitBinder
    public void registerCustomEditors(WebDataBinder binder, WebRequest request) {
        binder.registerCustomEditor(Boolean.class, new CustomBooleanEditor(false));
    }
}

if you are coming from Spring Roo basic configuration remember also to add this line in your webmvc-config.xml

    <context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>

like this:

<context:component-scan base-package="your.package.path" use-default-filters="false">
    <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    <context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>
</context:component-scan>
like image 23
Massimo Avatar answered Oct 27 '22 01:10

Massimo