jsfprimefacesselectonemenuconverters

Converter for list of objects that have as unique id a tuple of 2 member variables


I am trying to map a list of objects into a <p:selectOneMenu (code shown below):

Attribute.java

public class Attribute implements Serializable {
 private String name;
 private String type;
 private String value; //getters setters constructors ommitted

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Attribute attribute = (Attribute) o;
    return Objects.equals(name, attribute.name) &&
            Objects.equals(type, attribute.type);
}

@Override
public int hashCode() {
    return Objects.hash(name, type, value);
}

p:selectOneMenu code

 <p:selectOneMenu label="Existing Attributes" value="#{cellBean.selectedAttributeFromExistings}"
                                                 converter="attrConverter">
      <f:selectItem itemLabel="Existing attribute" itemValue="#{null}" itemDisabled="true"/>
      <f:selectItems value="#{cellBean.allAttributes}" var="attr" itemLabel="#{attr.name}" itemValue="#{attr}"/>
  <p:ajax event="change" update="@form" process="@form"/>
  </p:selectOneMenu>

Attribute converter (attrConverter) code

@FacesConverter(forClass=Attribute.class,  value = "attrConverter")
public class AttributeConverter implements Converter {

@Override
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String s) {
    String type = s.substring(s.indexOf(":") + 2, s.indexOf(","));
    String name = s.substring(s.indexOf("name: ") + 6 , s.length());
    Attribute attribute = new Attribute(type, name);
    return attribute;
}

@Override
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object attr) {
    if(attr == null) {
        return null;
    }
    else{
        Attribute attribute = new Attribute();
        String s = "";
        Iterator iterator = ((LinkedTreeMap)attr).keySet().iterator();
        while(iterator.hasNext()){
            String key = (String) iterator.next();
            String value = (String) ((LinkedTreeMap)attr).get(key);
            if(key.equals("name")){
                attribute.setName(value);
            }
            else if(key.equals("type")){
                attribute.setType(value);
            }

        }
        return attribute.toString();
    }
}

However, when I am selecting a value from the dropdown menu I am getting the following exception

Validation Error: Value is not valid : Validation Error: Value is not valid

error message

followed by an error message / pop on the top right of my screen, any idea what I am doing wrong?

PS: I have seen lots of examples that they use a unique id in order to retrieve the object through a DTO in the getAsObject method, in my example the uniqueness of the the Attribute object is the combination of type && value member variables.

Thanks in advance for any help :)


Solution

  • I don't know why you cast attr to LinkedTreeMap, you need to cast it to Attribute class. So your converter class should be:

    @FacesConverter(forClass=Attribute.class,  value = "attrConverter")
    public class AttributeConverter implements Converter {
    
     @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
    
            if (value != null && value.trim().length() > 0 && !value.isEmpty()) {
             /*I take your bean as ViewScoped */
                CellBean cellBean = (CellBean) context.getViewRoot().getViewMap().get("cellBean");
                for(Attribute a : cellBean.getAllAttributes()){
                    String combined = a.getType()+a.getValue();
                    if(combined.equals(value)){
                        return a;
                    }
                }
            }
            return null;
        }
    
        @Override
        public String getAsString(FacesContext context, UIComponent component, Object value)            {
            if (value != null) {
                Attribute a = (Attribute) value;
                return a.getType()+a.getValue();
            }
            return null;
        }
    }
    

    UPDATE

    Asker oriented:

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String s) {
        String[] typeAndName = s.split("#");
        return new Attribute(typeAndName[0], typeAndName[1]);
    
    }
    
    @Override
    public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object attr) {
        if(attr == null) {
            return null;
        }
        else{
            /* I assume your type and name don't contain '#' charachter */
            return ((Attribute)attr).getType()+"#"+((Attribute)attr).getName();
        }
    }