Primefaces selectManyCheckbox withing editable Datatable submitting null to Converter

2k views Asked by At

I am using a Primefaces selectManyCheckbox within an editable Datatable like this one. When the user clicks the edit button, he is able to select between different Documents in the Documents-Column. That is my code. Please note the converter I am using:

<p:dataTable id="tbl" styleClass="fixedLayoutTable no-highlight dblclick-edit edit-commit-others"
            value="#{cc.attrs.manager.mg}" var="mg"
             emptyMessage="#{msgs.noManager}" editable="true" widgetVar="tbl">
        <p:column  headerText="Documents">
            <p:cellEditor>
                <f:facet name="output">
                   <h:outputText value="#{msgs.noDocs}" rendered="#{mg.docs.size() == 0}" />
                <h:outputText value="#{mg.docs.toArray()[0].fileName}" rendered="#{mg.docs.size() > 0}" />
            </f:facet>
                <f:facet name="input">
                    <p:selectManyCheckbox id="chkDoc" 
                            value="#{mg.docs}" layout="pageDirection"
                            converter="myConverter" collectionType="java.util.HashSet">
                        <f:selectItems value="#{cc.attrs.manager.docs}"
                            var="d" itemValue="#{d}" itemLabel="#{d.fileName}" />
                    </p:selectManyCheckbox>
                </f:facet>
            </p:cellEditor>
        </p:column>
    </p:dataTable>

In another Tab of the application (lets call it Tab1) the user is able to upload some Documents. These documents were added to the cc.attrs.manager.docs-List in the backing bean, but won't get persisted in the database yet. If the user uploads some documents in Tab1 and goes to Tab2 (the tab with the datatable), he should be able to select between all documents including the recently added ones. So the user clicks on the edit button, all the documents appear in the SelectManyCheckbox. If the user selects these documents and clicks the ✔-Sign my Converter gets called.

I followed this example to implement the Converter. Here is a Code Snippet of this class:

@FacesConverter(value="myConverter")
public class MyConverter implements Converter {

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        if (component instanceof PickList) {
            return getAsObject((PickList)component, value);
        } else {
            return SelectItemsUtils.findValueByStringConversion(context, component, value, this);
        }
    }
}

This is working fine, until the user doesn't check the recently added documents. If he checks one of the new documents, the String-Parameter of the getAsObject-Method is "null". Does anyone have some suggestions, why this param is null? Is it because the document isn't persisted yet? Is it possible to get this working without persisting the documents added in Tab1?

2

There are 2 answers

0
joshi737 On BEST ANSWER

I solved it by implementing another converter which extends EntityConverter and checks if the ID is null. Thats my code like:

@FacesConverter(value = "anotherConverter")
public class AnotherConverter extends EntityConverter {

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if (value instanceof Document && ((Document) value).getId() == null) {
            return String.valueOf(((Document) value).hashCode());
        }
        return super.getAsString(context, component, value);
    }
}
8
Makky On

Instead of your own custom converter , try this generic converter which will work for everything.

import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.WeakHashMap;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

@FacesConverter(value = "entityConverter")
public class EntityConverter implements Converter {

    private static Map<Object, String> entities = new WeakHashMap<Object, String>();

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object entity) {
        synchronized (entities) {
            if (!entities.containsKey(entity)) {
                String uuid = UUID.randomUUID().toString();
                entities.put(entity, uuid);
                return uuid;
            } else {
                return entities.get(entity);
            }
        }
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
        for (Entry<Object, String> entry : entities.entrySet()) {
            if (entry.getValue().equals(uuid)) {
                return entry.getKey();
            }
        }
        return null;
    }

}