Tuesday, 13 October 2009

(Ice)faces hates me and Richard

After Richard Kolb and I have struggled with this for days, I finally cracked it.

We defined some dropdowns, and radio buttons in icefaces eg:
<ice:selectOneRadio id="reportColumnIndexRadio"
value="#{scratchPad.reportColumnIndex}"
partialSubmit="true"
layout="pageDirection">
<f:selectItems
value="#{scratchPad.reportColumnIndexSelectItemList}" />
</ice:selectOneRadio>

<ice:selectOneMenu id="reportColumnIndex"
value="#{scratchPad.reportColumnIndex}"
partialSubmit="true">
<f:selectItems
value="#{scratchPad.reportColumnIndexSelectItemList}" />
</ice:selectOneMenu>

but when a new item was selected nothing happened, we just got this VERY cryptic log entry e.g:
sourceId=reportEditorForm:j_id14[severity=(ERROR 2), 
summary=(reportEditorForm:j_id14:
An error occurred when processing your submitted information. ),
detail=(reportEditorForm:j_id14:
An error occurred when processing your submitted information. )]

After hours of (debugging, guessing, googling, repeat) loops,
I finally found this link which explains it a number of times:
http://www.icefaces.org/JForum/posts/list/7965.page

Faces or icefaces does not automagically serialise or cache objects when sending it to the browser!
It converts to/from Strings if it can
and tries to waste as much of your time as possible if it can't!


So my fix was setting the selectItem's value to our DB Id in stead of the object,
using the Id property for the SelectOne value.
Then on partial submit when the setter gets called,
we have to iterate through the cached list to find the one matching the Id to set the real property.

<ice:selectOneMenu id="reportColumnIndex"
value="#{scratchPad.reportColumnIndexId}"
partialSubmit="true">
<f:selectItems
value="#{scratchPad.reportColumnIndexSelectItemList}" />
</ice:selectOneMenu>

4 comments:

  1. Thank you for sharing this. I have spent with this issue only aprox. 3 hours, then I found your blog. Thank again.

    ReplyDelete
  2. I do not think this is the best solution, rather the common solution. It should a way to put an Object in the item value field

    ReplyDelete
  3. Andreas (orome@gmx.ch)06 August, 2010 11:01

    There is a much simpler solution imho:

    Create a converter (see code below) and attach the converter to the selectOneMenu component:



    Converter class:

    public class DefaultSelectItemConverter implements Converter
    {

    /**
    * Not explicitly documented.
    *
    * @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext,
    * javax.faces.component.UIComponent, java.lang.String)
    */
    public Object getAsObject(FacesContext fcontext, UIComponent comp, String valueString)
    {

    List children = comp.getChildren();
    for (UIComponent child : children)
    {
    if (child instanceof UISelectItem)
    {
    UISelectItem si = (UISelectItem) child;
    if (si.getValue().toString().equals(valueString))
    {
    return si.getValue();
    }
    }

    if (child instanceof UISelectItems)
    {
    UISelectItems sis = (UISelectItems) child;
    List items = (List) sis.getValue();
    for (SelectItem si : items)
    {
    if (si.getValue().toString().equals(valueString))
    {
    return si.getValue();
    }
    }
    }
    }
    throw new ConverterException("no conversion possible for string representation: "
    + valueString);
    }

    /**
    * Not explicitly documented.
    *
    * @see javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext,
    * javax.faces.component.UIComponent, java.lang.Object)
    */
    public String getAsString(FacesContext fcontext, UIComponent comp, Object value)
    {
    return value.toString();
    }

    }


    Have to be registered in faces-config.xml:

    defaultSIConverter
    my.package.DefaultSelectItemConverter

    ReplyDelete
  4. Thanks Andreas for your effective and simple solution.

    ReplyDelete