3

The following code creates a two radio buttons. Each option contains a date value that is successfully converted to a label of the format "yyyy-MM-dd". Once I make a selection and click the next button I get the following error "j_idt12:comDateChoice: Validation Error: Value is not valid". It seems simple enough but somethings wrong. Can any of you spot it?

I'm using JSF 2.0 in glassfish.

Backing bean

public List<SelectItem> getComDateList() {
    List<SelectItem> items = new ArrayList<SelectItem>();
    Calendar cal = GregorianCalendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, 1);
    cal.add(Calendar.MONTH, 1);
    Date nextFirst = cal.getTime();
    cal.add(Calendar.MONTH, 1);
    Date followingFirst = cal.getTime();
    items.add(new SelectItem(nextFirst, new SimpleDateFormat("yyyy-MM-dd").format(nextFirst)));
    items.add(new SelectItem(followingFirst, new SimpleDateFormat("yyyy-MM-dd").format(followingFirst)));
    return items;
}

JSF Code

<h:panelGrid columns="2">
                    <h:outputLabel value="#{msg.FinanceCommencementDate}" for="comDateChoice"/>
                    <h:selectOneRadio id="comDateChoice" value="#{signUpBean.current.commencementDate}" layout="pageDirection">
                        <f:convertDateTime type="date" dateStyle="short"/>
                        <f:selectItems  value="#{signUpBean.comDateList}"/>
                    </h:selectOneRadio>
                </h:panelGrid>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Wayne
  • 149
  • 3
  • 14

1 Answers1

4

This error will occur if the selected item value didn't pass the Object#equals() check on any of the available select item values. This can happen if the getter returned a different list during the apply request values phase of the form submit request than it did during the initial request to display the form.

Because you're reconstructing the list in the getter instead of constructing once in the constructor of a view scoped bean, the Date objects will get a different timestamp on every call, it will be some minutes/seconds in the future as compared to the initial Date objects. Hence the equals() will fail.

Move this logic into the constructor of the bean and rewrite the getter so that it does what it is supposed to do: return only the data. Do not do loading logic in a getter. You should also put the bean in the view scope so that the constructor doesn't re-run when you submit the form.

@ManagedBean
@ViewScoped
public class SignUpBean {

    private List<SelectItem> comDateList;

    public SignUpBean() {
        comDateList = new ArrayList<SelectItem>();
        // Fill it here.
    }

    public List<SelectItem> getComDateList() {
        return comDateList; // In getters, do nothing else than returning data!
    }

}

Update: the converter is also a potential source of the problem. You've basically instructed it to strip off the time when rendering the HTML page. So it uses the default time when converting back to Date. Either use

<f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss.SSS Z" />

or reset the time and timezone on the Calendar beforehand:

cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.setTimeZone(TimeZone.getTimeZone("GMT"));

this way you can use just a <f:convertDateTime type="date" />

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I'm going to have to read up on this....So I should make a separate bean @ViewScoped and just hold the list for this model and any other data that needs to be accessed my the view on this page? – Wayne Mar 23 '11 at 14:59
  • No, you can just use the same bean. You just need to move the logic in your getter into the constructor and mark the bean `@ViewScoped` (if it was `@RequestScoped`). I updated the answer with a kickoff example. – BalusC Mar 23 '11 at 15:01
  • Thanks, I've converted my code to ne similar to your example. The bean is @SessionScoped so I just left it like that. The error remains though. – Wayne Mar 23 '11 at 15:17
  • I made a whole new project to test this out using your framework above. No only does the converter not alter the text, it still has the same error. It gives a null converter error when I don't include it so the converter isn't being ignored completely. I've concluded that it may be a bug or something and am just using a string representation as I don't have time to faf with this any more. Thanks for the input. I learned something from it. – Wayne Mar 24 '11 at 12:15
  • The converter isn't applied on label. It's applied on the value. Thinking about this once again, this way seconds are omitted. Remove `type="date" dateStyle="short"` and use `` only. – BalusC Mar 24 '11 at 12:22
  • Oh ok, now I'm really confused. I thought the converter converted the label string to the object. That's worked for me on all my entity converters up till now anyway. No luck with your suggestion. I've pastebinned the whole project here if you want to have a look. It's as dead simple as I can get it. http://pastebin.com/QPFPWvRZ Thanks for the input. – Wayne Mar 24 '11 at 19:57
  • Converters only convert the value to and from `String` (because HTTP/HTML doesn't understand Java objects). You've already converted the label with `SimpleDateFormat` in backing bean code. – BalusC Mar 24 '11 at 19:58
  • Ah, I see it now (in the source). value="2011/04/01". That shouldn't be giving this much of a head ache. – Wayne Mar 24 '11 at 20:08
  • 1
    I tested it locally and the default conversion doesn't include microseconds and hence validation fails (which means that my previous comment is invalid, sorry about that). I changed it to a fixed pattern which includes every part and it works: ``. From the other hand on, you can also zero out the hours/minutes/seconds/milliseconds on the `Calendar` object you have there since they are irrelevant to you. – BalusC Mar 24 '11 at 20:09
  • Haha, can you believe it. Your a legend. Thanks. – Wayne Mar 24 '11 at 20:13