1
votes

When the f:selectItems tag in h:selectOneMenu gets fed a Set, the f:ajax tag works and the h:outputText thereafter gets updated with the selected value. But when the f:selectItems tag receives a Map, the f:ajax seems not to do anything anymore. I've created a minimal example for demonstration.

In the first snipped the value for f:selectItems yields a Set and everything works as expected:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
  <h:head>
    <title>SET</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  </h:head>
  <h:body>
    <h:form>
      <h:selectOneMenu value="#{setVsMap.string}">
        <f:selectItems value="#{setVsMap.strings}"/>
        <f:ajax render="someId"/>
      </h:selectOneMenu>
    </h:form>
    <div>
      <h:outputText value="String: "/>
      <h:outputText id="someId" value="#{setVsMap.string}"/>
    </div>
  </h:body>
</html>

In the second (almost identical) snippet the f:selectItems value yields a map. And this time the h:outputText (with 'someId') is not rendered:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
  <h:head>
    <title>MAP</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  </h:head>
  <h:body>
    <h:form>
      <h:selectOneMenu value="#{setVsMap.date}">
        <f:selectItems value="#{setVsMap.dates}"/>
        <f:ajax render="someId"/>
      </h:selectOneMenu>
    </h:form>
    <div>
      <h:outputText value="Date: "/>
      <h:outputText id="someId" value="#{setVsMap.date}"/>
    </div>
  </h:body>
</html>

This is the backing bean for both pages:

@Named
@ViewScoped
public class SetVsMap implements Serializable {

  private String string; // getter and setter omitted
  private Date date; // getter and setter omitted
  private Set<String> strings;
  private Map<String, Date> dates;

  @PostConstruct
  public void init() {
    strings = new LinkedHashSet<>();
    strings.add("foo");
    strings.add("bar");
    strings.add("baz");

    dates = new LinkedHashMap<>();
    Calendar c = Calendar.getInstance();
    c.add(Calendar.DATE, -1);
    dates.put("Yesterday", c.getTime());
    c.add(Calendar.DATE, 1);
    dates.put("Today", c.getTime());
    c.add(Calendar.DATE, 1);
    dates.put("Tomorrow", c.getTime());
  }

  public Set<String> getStrings() {
    return strings;
  }

  public Map<String, Date> getDates() {
    return dates;
  }
}

Debugging shows, that the setString method gets called, whereas the setDate method is never called.

Why is this? How to fix it?

Tested on glassfish 4.1.2 with mojarra 2.2.13.

1
Add a h:messages to the page and run in development mode... Any errors then? And does a Map<String, String> work?Kukeltje
The project is in developement mode. h:messages shows no errors. A Map<String, String> is indeed working.user1785730
The Map<String, String> hint gave me an idea: I think I have to use a converter for Date, because the date part is converted by JSF to a string on the frontend. I will try this and report back.user1785730
I would have expected an error in dev mode, but this is indeed what I was thinking of but not directly told you ('Simplification' is always something to try and a String is more simple than a Date)... I always do these things in creating a minimal reproducible example... Helps a lot... find causes quickly and reduces the time/prevents the need to write questions in StackoverflowKukeltje
It was indeed the Date object, that wasn't properly converted. I decided to use strings only and do the conversion in java.user1785730

1 Answers

0
votes

You can either implement your own converter for Date types to display their String values or use f:convertDateTime tag i.e:

<h:selectOneMenu value="#{setVsMap.string}">
    <f:selectItems value="#{setVsMap.strings}"/>
    <f:convertDateTime pattern="d-M-yyyy" />
    <f:ajax render="someId"/>
</h:selectOneMenu>

<h:outputText id="someId" value="#{setVsMap.date}" >
    <f:convertDateTime pattern="d-M-yyyy" />
</h:outputText>