How to put Java Map into Optgroup Options Using Spring MVC?

4k views Asked by At

I am using Spring MVC.

I currently have a Map being passed from Java to my page in a variable called "userLocales" via request.setAttribute('userLocales', bluh bluh bluh)

I need to somehow get the strings in that variable to build a list of option elements in an optgroup select element. My initial thought would be to somehow turn this Map into a Javascript object somehow, and then add the strings to newly created option elements to be placed in the optgroup element.

The optgroup element is static and already created. I just need the options with the string in them.

2

There are 2 answers

0
byteWalrus On BEST ANSWER

And I think I found the answer right after I posted this question. Wonderful

Source : http://www.imrantariq.com/blog/option-group-capability-for-spring-mvc-form-taglib/

The “optgroup” attribute when set would first do a sort by whatever “expression” is defined in “optgroup”, and then would track the progression of iteration and insert the “optgroup” html start tag, and end tags where appropriate in the select list using the expression in “optgroup” as the label.

Unfortunately, currently spring mvc is not offering such functionality through its tags. This can be achieved through a nice logic given below. You need to send a hashMap from controller.

Map<String, ArrayList<String>>

In this map, string is key i.e. group name and arraylist is the number of values that lies in this group. Jsp code can be written in this way.

<form:select multiple="single" path="itemType" id="itemType">
    <form:option value="0" label="Select" />
    <c:forEach var="itemGroup" items="${itemTypeList}" varStatus="itemGroupIndex">
       <optgroup label="${itemGroup.key}">
           <form:options items="${itemGroup.value}"/>        
       </optgroup>
    </c:forEach>        
</form:select>
0
gene b. On

Controller

Note the collection is Map<String, List<KeyValueBean>>.

It stores e.g. Group 1 -> { ("Option 1.1 Label","OPTION_1_1_VAL"), ("Option 1.2 Label","OPTION_1_2_VAL"), ..}

@ModelAttribute("careerOptions")
Map<String, List<KeyValueBean>> getCareerOptions() {        
    HashMap<String, List<KeyValueBean>> result = new HashMap<String, List<KeyValueBean>>();
    result.put("Grp1", new ArrayList<KeyValueBean>());
    result.get("Grp1").add(new KeyValueBean("Option 1.1", "OPT_1_1"));
    result.get("Grp1").add(new KeyValueBean("Option 1.2", "OPT_1_2"));
    result.put("Grp2", new ArrayList<KeyValueBean>());
    result.get("Grp2").add(new KeyValueBean("Option 2.1", "OPT_2_1"));

    return result;
}       

JSP

<form:select path="careerSelected" id="careerElement">
    <form:option label="" value="" />
    <c:forEach var="optionGroup" items="${careerOptions}">
       <optgroup label="${optionGroup.key}">
       <c:forEach var="option" items="${optionGroup.value}">
          <form:option label="${option.key}" value="${option.value}" />                             
       </c:forEach>                                                          
       </optgroup>
    </c:forEach>
</form:select>

KeyValueBean (I created my own, but Java's built-in one is AbstractMap.SimpleEntry<K,V> -- you can use that one, it's just the code is a bit uglier)

public class KeyValueBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private String key;
    private String value;

    public KeyValueBean(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }

}

One other case: Sometimes the Select items are MIXED (Flat mixed with OptGroup)

A
B
C (subgroup)
  - C.1
  - C.2

In this case, the Collection can be Map where every entry will be keyed to an Object : either (1) a String or (2) a HashMap, which we will discover from the JSP.

Controller for a MIXED Data Structure

@ModelAttribute("careerOptionsMixed")
Map<String, Object> getCareerOptionsMixed() {

    LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();

    result.put("Flat Option 1", "OPT_1_FLAT");
    result.put("Group Option 2", myHashMap); // Fill out your HashMap for Group (Key->Value) and add it here
    result.put("Flat Option 3", "OPT_3_FLAT");

    return result;
}           

JSP for the MIXED Data Structure (trick required to determine Collection or not, done via iterating and setting a var) In the JSP we'll need to identify whether we're getting a Collection or a String. The only way to do this is via iteration and setting a variable.

<form:select path="career" id="careerField">
   <form:option label="" value="" />
   <c:forEach var="optionOrOptionGroup" items="${careerOptionsMixed}">
      <%--  Must use iteration to find out if this is a Collection or not: https://stackoverflow.com/a/1490171/1005607 --%>
      <c:set var="collection" value="false" />
      <c:forEach var="potentialOptionGroup" items="${optionOrOptionGroup.value}" varStatus="potentialOptionGroupStatus">
         <c:if test="${potentialOptionGroupStatus.index > 0}">
            <c:set var="collection" value="true" />
         </c:if>
      </c:forEach>
      <c:choose>
         <c:when test="${collection eq true}">
            <%--  Now we know this is a LinkedHashMap --%>
            <optgroup label="${optionOrOptionGroup.key}">
               <c:forEach var="optionGroup" items="${optionOrOptionGroup.value}">
                  <form:option label="${optionGroup.key}" value="${optionGroup.value}" />
               </c:forEach>
            </optgroup>
         </c:when>
         <c:otherwise>
            <%--  Now we know this is a flat String --%>
            <form:option label="${optionOrOptionGroup.key}" value="${optionOrOptionGroup.value}" />
         </c:otherwise>
      </c:choose>
   </c:forEach>
</form:select>