views:

571

answers:

2

Hi,

Suppose i have a month, day and year select. One select for each one. And now i need to bind them to a single backing bean property - java.util.Date. How do i get my goal ?

A: 

When saying "binding" and "backing bean", you are supposed to refer to the following:

<h:inputText binding="#{myBean.myTextField}" />

and have private UIInput in your bean.

If that is the case - no, you can't bind like that. Well, I'm not sure if you technically can - but it will surely have unexpected effect.

If, however, you want to target a managed bean property, then you can, for example:

<h:inputText value="#{myBean.myProperty.day}" />
<h:inputText value="#{myBean.myProperty.year}" />
Bozho
Thanks for your reply. Any workaround ?
Arthur Ronald F D Garcia
why would you need that? What's the usecase? What's the actual component you want to bind to?
Bozho
+1  A: 

Three ways:

  1. Back or intermediate it by java.util.Calendar with three getters and three setters.
  2. Make use of a Converter, this is however going to be a bit hacky.
  3. Make use of a 3rd party component like rich:calendar.

Edit: as per the comments, here's how option 2 would look like.

page.jsp:

<h:form>
    <h:selectOneMenu value="#{myBean.date}">
        <f:converter converterId="datePartConverter" />
        <f:attribute name="part" value="day" />
        <f:selectItems value="#{myBean.days}" />
    </h:selectOneMenu>
    <h:selectOneMenu value="#{myBean.date}">
        <f:converter converterId="datePartConverter" />
        <f:attribute name="part" value="month" />
        <f:selectItems value="#{myBean.months}" />
    </h:selectOneMenu>
    <h:selectOneMenu value="#{myBean.date}">
        <f:converter converterId="datePartConverter" />
        <f:attribute name="part" value="year" />
        <f:selectItems value="#{myBean.years}" />
    </h:selectOneMenu>

    <h:commandButton value="submit" action="#{myBean.submit}"/>
    <h:messages />
</h:form>

mypackage.MyBean:

package mypackage;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.faces.model.SelectItem;

public class MyBean {

    private static List<SelectItem> days = new ArrayList<SelectItem>();
    private static List<SelectItem> months = new ArrayList<SelectItem>();
    private static List<SelectItem> years = new ArrayList<SelectItem>();

    static {
        // Just do your thing to fill them. Only ensure that those are Strings,
        // else you'll need to change the type in Converter accordingly.
        for (int i = 1; i <= 31; i++) days.add(new SelectItem(String.valueOf(i)));
        for (int i = 1; i <= 12; i++) months.add(new SelectItem(String.valueOf(i)));
        for (int i = 2000; i <= 2020; i++) years.add(new SelectItem(String.valueOf(i)));
    }

    private Date date;

    public void submit() {
        // Print submitted date to stdout.
        System.out.println("Submitted date: " + date);
    }

    public List<SelectItem> getDays() {
        return days;
    }

    public List<SelectItem> getMonths() {
        return months;
    }

    public List<SelectItem> getYears() {
        return years;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

}

mypackage.DatePartConverter:

package mypackage;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

public class DatePartConverter implements Converter {

    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        String part = (String) component.getAttributes().get("part");
        Date date = null;

        if (context.getRenderResponse()) {
            // Convert any default/selected date for display.
            Date selectedDate = (Date) ((UIInput) component).getValue();
            if (selectedDate != null) {
                if (("day".equals(part) && new SimpleDateFormat("d").format(selectedDate).equals(value))
                    || ("month".equals(part) && new SimpleDateFormat("M").format(selectedDate).equals(value))
                    || ("year".equals(part) && new SimpleDateFormat("yyyy").format(selectedDate).equals(value)))
                {
                    date = selectedDate;
                }
            }
        } else {
            // Convert submitted date after submit.
            Map<String, Object> map = context.getExternalContext().getRequestMap();
            if ("day".equals(part)) {
                map.put("DatePartConverter.day", value); // Save until we have all parts.
            } else if ("month".equals(part)) {
                map.put("DatePartConverter.month", value); // Save until we have all parts.
            } else if ("year".equals(part)) {
                String day = (String) map.get("DatePartConverter.day");
                String month = (String) map.get("DatePartConverter.month");
                String dateString = String.format("%s-%s-%s", day, month, value);

                try {
                    date = new SimpleDateFormat("d-M-yyyy").parse(dateString);
                } catch (ParseException e) {
                    throw new ConverterException(new FacesMessage(e.getMessage()), e);
                }
            }
        }

        return date;
    }

}

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        // Not relevant here. Just return SelectItem's value.
        return (String) value;
    }

faces-config.xml

<converter>
    <converter-id>datePartConverter</converter-id>
    <converter-class>mypackage.DatePartConverter</converter-class>
</converter>

<managed-bean>
    <managed-bean-name>myBean</managed-bean-name>
    <managed-bean-class>mypackage.MyBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

Note that there's no Validator and that SimpleDateFormat is by default lenient. Thus, selecting for example 31 november would produce 1 december. You may need to implement a DatePartValidator yourself if you want to warn about that.

BalusC
Thanks for your reply, BalusC (+1) Can you show how do i get my goal bu using approach number 2: Make use of a Converter, this is however going to be a bit hacky
Arthur Ronald F D Garcia
I've edited my question with a working sample.
BalusC
Note: you really need to put the dropdowns in the order of day-month-year. If you for example want year-month-day, then you'll need to rearrange the if blocks in the converter so that the last part will finally create the date based on the input. Talking about hacky :)
BalusC
Thank you very much.
Arthur Ronald F D Garcia