1

I came across a scenario were a set of radio buttons gets reset to a none-selected state whenever the view returns from an immediate action, although the actual value is processed correctly. Refreshing the view also displays the correct value. Weirdly enough, the behavior does only occur when the component uses an converter and is not inside a ui:repeat I narrowed it down to the following example:

JSF Page

<ui:repeat value="#{bean.stringList}" var="item" varStatus="iteration">        
    #{item} 
    <h:selectOneRadio value="#{bean.boolValue}" rendered="#{iteration.last}">
        <f:selectItems value="#{bean.boolItemList}" />
        <f:converter converterId="testConverter" />      
    </h:selectOneRadio><br /><br />
          
</ui:repeat>
<hr /> 
<h:selectOneRadio value="#{bean.stringValue}">
    <f:selectItems value="#{bean.stringItemList}" />                 
</h:selectOneRadio>    

<h:selectOneRadio value="#{bean.boolValue}">
    <f:selectItems value="#{bean.boolItemList}" />
    <f:converter converterId="testConverter" />        
</h:selectOneRadio>

<p>
<h:commandButton value="Submit" type="submit" />
<h:commandButton value="Do nothing, but immediate!" immediate="true" action="#{bean.doNothing}" />
</p>

<p>boolValue is: #{bean.boolValue}</p>

Backing Bean

@ManagedBean(name = "bean")
@SessionScoped
public class Bean {

private List<String> stringList;
private List<SelectItem> stringItemList;
private List<SelectItem> boolItemList;
private boolean boolValue;    
private String stringValue;    
private int removeParam;

public Bean() {
    stringList = new ArrayList<String>();
    stringList.add("Test Item One");      
    stringList.add("Test Item Two");  
    
    stringItemList = new ArrayList<SelectItem>();
    stringItemList.add(new SelectItem("1", "Option One"));
    stringItemList.add(new SelectItem("2", "Option Two"));      
    
    boolItemList = new ArrayList<SelectItem>();
    boolItemList.add(new SelectItem(Boolean.TRUE, "This is real"));
    boolItemList.add(new SelectItem(Boolean.FALSE, "This is not real"));                                                                    
    }
// + getters and setters

public void doNothing(){
    }
}

Converter

@FacesConverter("testConverter")
public class TestConverter implements Converter {

@Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{       
    if (value!= null) {
        if (value.equals("real"))
        { 
        return Boolean.TRUE;          
        }
    }
    return Boolean.FALSE;
}

@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
    boolean check = (Boolean) value;
    if (check)  return "real";
    else        return "unreal";
    }
}

So we have:

  • a group of radio buttons built from a backing bean list of SelectItem(String, String), saved to a String. No converter needed.
  • a group of radio buttons built from a backing bean list of SelectItem(boolean, String), saved to a boolean. A converter is provided to map the boolean to some words ("real"/"unreal") - just to replace the standard converter ("true"/"false")
  • A submit button + output to test if values get set correctly.
  • A button with some immediate action, which does nothing else but change the JSF lifecycle.

With this, one can play around and observe the following:

  • Whenever the page returns from the immediate action, the radios with converter outside ui:repeat display no value, meaning: no radio is selected
  • However, the value is actually set correctly during submit. Refreshing the page will results in correctly set radios.
  • the same radio group inside ui:repeat works just fine, as does the one without the converter

Testing around against better knowledge I checked that the converter-radios put inside a <c:foreach> instead of ui:repeat show the same error (which is as I would have expected, as it merely builds the tree and then vanishes) and that attaching some standard converter to the string-radios does not make them break. Regarding BalusC's comment, changing the bean to ViewScoped does not affect the problem - only then, refresh gives you a blank default-initialized page.

Here I am at the end of my knowledge. I don't really see how to reduce this any further or why it behaves like this. So... well... ideas anybody?

Specs I got from the server guy: Mojarra JSF API Implementation 2.0.5-FCS, JSF 2.0 and Facelets, Server is a Weblogic 11g 10.3

Community
  • 1
  • 1
Louise
  • 1,451
  • 1
  • 18
  • 40
  • 1
    What scope is the bean put in? Why are the inputs inside the `` not bound to the `#{item}` but instead all to one and same property? – BalusC Oct 23 '12 at 15:57
  • I use the testBean for various things on different pages (dummy values & functions) so it is session scoped. The "real" bean is currently also session scoped, but this is a left over from early days of the project. I haven't yet found the time to make them viewScoped as they (afaik) should be (they are beans for a specific page). – Louise Oct 24 '12 at 09:23
  • The ui:repeat is just for show, `#{item}` is not used because it is just a dummy string, no real data object. Again, on the "real" page, the repeat is generated from a list of data objects (e.g. `generator`) and uses their specific fields, e.g. `generator.type`. To select `type` there is a two-choice radio group, converting to and from another object that reflects the type. The fact that this radio group works, while others outside the repeat don't, led me to the (reduced) scenario above. – Louise Oct 24 '12 at 09:25
  • I re-did the example to be more of self-contained (no special data objects needed). It should now be possible to copy and run it elsewhere. – Louise Oct 25 '12 at 13:14

1 Answers1

0

I still have no real understanding of why it behaves this way, but I found two solutions.

1) The doNothing() method does not comply with the spec, which demands that the signature must match java.lang.Object action()

If I correctly add a return value to go back to the same page (either through the file name "test.xhtml" or through outcome and navigation-rule in the faces-config), everything works fine.

2) If I add f:ajax to the button to execute and re-render part of the page everything is well. I played around a bit and it does not seem to matter what part of the page is executed/re-rendered (other than the normal effects in applying values and such).

I am still a bit confused by the initially rather random behavior (and how ui:repeat and converters influence it). It seems that my understanding of how JSF returns to a page without explicit navigation is incomplete. This also applies to getting back after failed validation via FacesContext#renderResponse. Any help appreciated (I could also open a new question if appropriate and then edit this answer)

Louise
  • 1,451
  • 1
  • 18
  • 40