Search code examples
jsfenumsselectmanycheckbox

SelectManyBox - Enum Not saving


I still have problem with selectManyCheckBox..

selectManyCheckBox:

 <p:selectManyCheckbox converter="genericEnumConverter" value="#{aView.newObject.aValue}">                                           
        <f:selectItems value="#{enumBean.aValueValues}" var="s" itemValue="#{s}" itemLabel = "#{s.name}"/>
 </p:selectManyCheckbox> 

Converter for this selectManyCheckBox is the same as described here: Use enum in h:selectManyCheckbox

@FacesConverter("genericEnumConverter")
public class GenericEnumConverter implements Converter {

    private static final String ATTRIBUTE_ENUM_TYPE = "GenericEnumConverter.enumType";

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        System.out.println("getAsString 1: ");
        if (value instanceof Enum) {
            System.out.println("getAsString 2: ");
            component.getAttributes().put(ATTRIBUTE_ENUM_TYPE, value.getClass());
            System.out.println("getAsString 3: ");
            return ((Enum<?>) value).name();
        } else {
            System.out.println("getAsString 4: ");
            throw new ConverterException(new FacesMessage("Value is not an enum: " + value.getClass()));
        }        
    }

    @Override
    @SuppressWarnings({"rawtypes", "unchecked"})
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        System.out.println("getAsObject 1: ");
        Class<Enum> enumType = (Class<Enum>) component.getAttributes().get(ATTRIBUTE_ENUM_TYPE);
        System.out.println("getAsObject 2: ");
        try {
            System.out.println("getAsObject 3: ");
            return Enum.valueOf(enumType, value);
        } catch (IllegalArgumentException e) {
            System.out.println("getAsObject 4: ");
            throw new ConverterException(new FacesMessage("Value is not an enum of type: " + enumType));
        }
    }

The enum is:

public enum aValue {

        1Value,
        2Value,
        3Value,
        4Value;
        private final String name;

        private aValue() {
            System.out.println("aValue 1");
            this.name = null;
            System.out.println("aValue 2");
        }

        public String getName() {
            System.out.println("getName 1 " + name());
            return ResourceBundleUtil.getLabelFromRb("aValue." + name());
        }
    }

    public aValue[] getAValueValues() {
        return AValue.values();
    }

Tomcat Logs are:

aValue 1
aValue 2
aValue 1
aValue 2
aValue 1
aValue 2
aValue 1
aValue 2
aValue 1
aValue 2
aValue 1
aValue 2
aValue 1
aValue 2
aValue 1
aValue 2
aValue 1
aValue 2
getName 1Value
getName 2Value
getName 3Value
getName 4Value
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getName 1Value
getName 2Value
getName 3Value
getName 4Value
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 
getAsString 1: 
getAsString 2: 
getAsString 3: 

When I press save button, nothing happens and nothing is saved into database. It looks like it is not going into getAsObject method. I don't know why. When I change the component into a SelectOneMenu there is no problem. But this selectManyCheckBox thing is not working. Does anyone have any idea?


Solution

  • Your problem is here:

    <p:selectManyCheckbox ... value="#{aView.newObject.aValue}">
    

    You're binding the value attribute of an input component which can get/set multiple values to a property which represents a single value. This isn't going to work. You need to bind the value attribute to a collection (e.g. List<aValue>) or array (e.g. aValue[]) property instead of a single property aValue.

    For example:

    public class NewObject {
    
        private List<aValue> aValues;
    
        // ...
    }
    

    with

    <p:selectManyCheckbox ... value="#{aView.newObject.aValues}">
    

    Or, alternatively:

    public class NewObject {
    
        private aValue[] aValues;
    
        // ...
    }
    

    with

    <p:selectManyCheckbox value="#{aView.newObject.aValues}">
    

    Note that when you use arrays, the whole converter is not needed. See also omnifaces.GenericEnumConverter showcase page.


    Unrelated to the concrete problem, according to Java Naming Conventions, enum class names should start with uppercase, like as with regular classes and interfaces, so aValue as enum class name is really a bad choice. Hungarian notations are also discouraged in Java, by the way.


    Update: to prove its working, here's an SSCCE.

    The enum:

    public enum Value {
    
        ONE, TWO, THREE;
    
        public String getName() {
            return name().toLowerCase();
        }
    
    }
    

    The entity:

    public class Entity {
    
        private Value[] values;
    
        public Value[] getValues() {
            return values;
        }
    
        public void setValues(Value[] values) {
            this.values = values;
        }
    
    }
    

    The managed bean:

    @ManagedBean
    @RequestScoped // Note: @ViewScoped works as well on this SSCCE.
    public class Bean {
    
        private Entity entity;
    
        @PostConstruct
        public void init() {
            entity = new Entity();
        }
    
        public void submit() {
            System.out.println(Arrays.toString(entity.getValues()));
        }
    
        public Entity getEntity() {
            return entity;
        }
    
        public Value[] getAvailableValues() {
            return Value.values();
        }
    
    }
    

    The view (without any converter!):

    <h:form>
        <p:selectManyCheckbox value="#{bean.entity.values}">
            <f:selectItems value="#{bean.availableValues}" var="value"
                itemValue="#{value}" itemLabel="#{value.name}" />
        </p:selectManyCheckbox> 
        <p:commandButton value="Submit" action="#{bean.submit}" /> 
    </h:form>
    

    It works just fine for me (Mojarra 2.1.28 + PrimeFaces 4.0, but should work on all older versions as well as this construct isn't previously known to have any bugs).