4

one more time i'm in trouble here. My point is: In my project i need a converter for (obviously) convert the items from the SelectOneMenu component to a list property in the respective bean. In my jsf page i have:

<p:selectOneMenu id="ddlPublicType" value="#{publicBean.selectedPublicType}"    effect="fade" converter="#{publicBean.conversor}" > 
    <f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt" itemLabel="#{pt.label}" itemValue="#{pt.value}"></f:selectItems>
</p:selectOneMenu>

And my bean is:

@ManagedBean(name = "publicBean")
@RequestScoped
public class PublicBean {

// Campos
private String name; // Nome do evento
private TdPublicType selectedPublicType = null;
private List<SelectItem> lstPublicTypes = null;
private static PublicTypeDAO publicTypeDao; // DAO 

static {
    publicTypeDao = new PublicTypeDAO();
}
// Construtor

public PublicoBean() {

    lstPublicTypes = new ArrayList<SelectItem>();
    List<TdPublicType> lst = publicTypeDao.consultarTodos();
    ListIterator<TdPublicType> i = lst.listIterator();
    lst.add(new SelectItem("-1","Select..."));
    while (i.hasNext()) {
        TdPublicType actual = (TdPublicType) i.next();
        lstPublicTypes.add(new SelectItem(actual.getIdPublicType(), actual.getNamePublicType()));
    }

}

// Getters e Setters

...

public Converter getConversor() {
    return new Converter() {
        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
            // This value parameter seems to be the value i had passed into SelectItem constructor
            TdPublicType publicType = null; // Retrieving the PublicType from Database based on ID in value parameter
            try {
                if (value.compareTo("-1") == 0 || value == null) {
                    return null;
                }
                publicType = publicTypeDao.findById(Integer.parseInt(value));
            } catch (Exception e) {
                FacesMessage msg = new FacesMessage("Error in data conversion.");
                msg.setSeverity(FacesMessage.SEVERITY_ERROR);
                FacesContext.getCurrentInstance().addMessage("info", msg);
            }
            return publicType;
        }

        @Override
        public String getAsString(FacesContext context, UIComponent component, Object value) {
            return value.toString(); // The value parameter is a TdPublicType object ?
        }
    };
}

...
}

In the getAsObject() method, the value parameter seems to be the value i had passed into SelectItem constructor. But in the getAsString() method, the value also seems to be a string representation of an Id. This parameter shouldn't be of type TdPublicType ? There is anything wrong in my code?

TheEwook
  • 11,037
  • 6
  • 36
  • 55
mnatan.brito
  • 883
  • 5
  • 18
  • 32

3 Answers3

4

The getAsString() should convert the Object (which is in your case of type TdPublicType) to a String which uniquely identifies the instance, e.g. some ID, so that it can be inlined in HTML code and passed around as HTTP request parameters. The getAsObject() should convert exactly that unique String representation back to the concrete Object instance, so that the submitted HTTP request parameter can be converted back to the original object instance.

Basically (trivial prechecks and exception handling omitted):

@Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
    // Convert Object to unique String representation for display.
    return String.valueOf(((TdPublicType) modelValue).getId());
}

@Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
    // Convert submitted unique String representation back to Object.
    return tdPublicTypeService.find(Long.valueOf(submittedValue));
}

Update: you've another problem, you're specifying the value property of TdPublicType class as the item value instead of the TdPublicType instance itself. This way the converter will retrieve the value property instead of the TdPublicType instance in the getAsString(). Fix it accordingly:

<f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt" 
    itemLabel="#{pt.label}" itemValue="#{pt}"/>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Do i need overload the **toString()** of TdPublicType? Inside the **getAsString()** method is generated an exception saying that was impossible to cast from String to TdPublicType. – mnatan.brito Aug 31 '12 at 13:21
  • Then the model value is apparently not of type `TdPublicType`. And indeed, you used `itemValue="#{pt.value}"` instead of `itemValue="#{pt}"`. Fix it accordingly. – BalusC Aug 31 '12 at 13:23
  • He still is returning an Integer as the backing model of the component, but i already has altered the itemValue property to #{pt}. – mnatan.brito Aug 31 '12 at 13:52
  • I figured out why the value paramater of **getAsString()** is returning an Integer and not a TdPublicType object, i need to have a List not List as the value property of . – mnatan.brito Aug 31 '12 at 15:15
  • Yes, that's correct. The `List` is the old JSF 1.x way. See also http://stackoverflow.com/tags/selectonemenu/info – BalusC Aug 31 '12 at 15:36
0

Now the code is working. My error was in the loading method. I was doing this:

// Loading menu
List<TdPublicType> l = daoPublicType.retrieveAll();
Iterator<TdPublicType> i = l.iterator();
while (i.hasNext()) {
    TdPublicType actual = (TdPublicType) i.next();
    lstMenuPublicType.add(new SelectItem(actual.getIdtPublicType(), actual.getNamePublicType()));
}

But the right way is:

// Loading menu
List<TdPublicType> l = daoPublicType.retrieveAll();
Iterator<TdPublicType> i = l.iterator();
while (i.hasNext()) {
    TdPublicType actual = (TdPublicType) i.next();
    lstMenuPublicType.add(new SelectItem(actual, actual.getNamePublicType())); // In the first parameter i passed the PublicType object itself not his id.
} 
mnatan.brito
  • 883
  • 5
  • 18
  • 32
0

use can use generic converter which will convert the value in the backing bean. You do not need any casting also.

@FacesConverter(value = "GConverter")
public class GConverter 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;
    }

}

Example usage would be

<p:selectOneMenu id="ddlPublicType" value="#{publicBean.selectedPublicType}"    effect="fade" converter="GConverter" > 
    <f:selectItems value="#{publicoBean.lstPublicTypes}" var="pt" itemLabel="#{pt.label}" itemValue="#{pt}"></f:selectItems>
</p:selectOneMenu>
Maverick
  • 131
  • 1
  • 4
  • Warning: this converter is memory inefficient and may cause memory leaks when 2nd level database cache is used. It's NOT recommended for production environments. See also http://stackoverflow.com/q/30186849 – BalusC Jun 03 '16 at 10:15
  • no glitches till now , we have been using this converter for quite a long time. however, thanks for the information . we will certainly look for some other options as well – Maverick Jun 03 '16 at 18:48