mario.fts
mario.fts

Reputation: 21

Optional Calendar type field in JSF form

I want to make a Calendar input in a JSF form optional. When the user doesn't inform the birth, the Calendar field must stay null.

My code:

@Model
public class Registration {
private User user = new User();

    public void register() {
        // stuff
    }
//getUser
}

The model class:

public class User { 
    //other fields
    private Calendar birth;
    // gets and sets
}

The page:

<h:outputLabel for="birth" value="Birth Date" />
<h:inputText id="birth" value="#{registration.user.birth.time}" >
    <f:convertDateTime pattern="dd/MM/yyyy" />
</h:inputText>

The problem here so far is the <f:convertDateTime>, that depends of the getTime() method of Calendar to make the conversion. It throws a NPE (Null Pointer Exception) when executed. I know that if I initialize the birth the problem is solved, but I really want the field to be null when the user does not fill the field.

Any thoughts of a way to achive this?

Upvotes: 1

Views: 806

Answers (2)

mario.fts
mario.fts

Reputation: 21

well, I'll post this for posterity. I think this would do the job:

@FacesConverter("CustomCalendarConverter")
public class CalConverter implements Converter{

@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
    if(value != null && !value.toString().isEmpty()){
        try {
            String pattern = (String) component.getAttributes().get("pattern");
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new SimpleDateFormat(pattern).parse(value));
            return calendar;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    return null;
}

@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {

    if(value != null && !value.toString().isEmpty()){
        Calendar cal = (Calendar) value;
        String pattern = (String) component.getAttributes().get("pattern");
        return new SimpleDateFormat(pattern).format(cal.getTime());
    }
        else return null;
    }
}

And the view should be:

<h:outputLabel for="birth" value="Birth Date" />
<h:inputText id="birth" value="#{registration.user.birth}">
    <f:converter converterId="CustomCalendarConverter" />
    <f:attribute name="pattern" value="dd/MM/yyyy" />
</h:inputText>

You can create a composite component too, something like <custom:inputCalendar> for instance.

Upvotes: 1

landal79
landal79

Reputation: 1520

The problem is not related to the convert, but to the expression:

#{registration.user.birth.time}

Infact this is DateTimeConverter code:

 public Object getAsObject(FacesContext context, UIComponent component,
                          String value) {

    ...

    Object returnValue = null;
    DateFormat parser = null;

    try {

        // If the specified value is null or zero-length, return null
        if (value == null) {
            return (null);
        }
        value = value.trim();
        if (value.length() < 1) {
            return (null);
        }

        ...
         returnValue = parser.parse(value);

     } catch (ParseException e) {
       ...
     }
     return returnValue;
  }

As you can notice, null and empty values are managed rightly. But when the values are applied, the expression birth.time is evaluated, but the birth reference is null, so when the setter on time is called you get NPE.

A possibile solution is to bind the birth field to java.util.Date type directly, which is the type managed by the DateTimeConverter, as below:

public class User { 
    //other fields
    private Date birth;
    // gets and sets
} 

Upvotes: 1

Related Questions