2014-10-03 114 views
0

我有一个HashMap它看起来很像这个..如何将HashMap键映射到thymeleaf中的select?

class MyDTO 
{ 
private Map<Long, StringListWrapper> myMap=new HashMap<>(); 
private List<Long> keys=new ArrayList<>(); 
private List<String> values=new ArrayList<>(); 
// setter and getter methods 
} 
class StringListWrapper 
{ 
private List<String> st=new ArrayList<>(); 
// setter and getter methods 
} 

现在,对于地图的键,我有一个选择其中将包含键,并且将用于值的列表框。

<select th:field="*{myMap}"> 
<option th:each="key : *{keys} th:value="${key}" th:text="${key}"></option> 
</select> 

在此之上,keys参考keysMyDTO。我用它来显示<select>中的按键。但是,此选择必须映射到myMap以确保此处选择的选项充当键。

<select multiple="multiple"> 
<option th:each="value : *{values}" th:value="${value}" th:text="${value}"></option> 
</select> 

现在,也是这上面多选择必须映射到myMap使这里选择的选项进入StringListWrapper.st。现在,如何以及在哪里放置th:field属性以供多重选择?

在此先感谢。希望你能尽快回复。

回答

1

有不清楚的问题的几件事情,但是基于代码中,我假设myMap属性是Map,因为你将提供多对对使用这些选择字段来选择一些值密钥数量。

如果上面的假设是错误的,那么为什么有myMap作为Map而不是MyDTO的两个不同属性/属性?

与我的假设去,你需要选择对被转换成一个List或阵列MyDTO,并有一个创建基于列表的Map的吸气剂(getMyMap())。上面列表中的条目将是一个简单的值对象,其中Long keyList<String>作为其属性。这是这个用例的一个工作示例。看看Thymeleaf如何改变这种状况,你可能会发现一个适合你的解决方案的调整版本。

此外,次要的事情:恕我直言,我不认为keysvalues属性在MyDTO之内是合适的,但它可能只是简化了这个问题。它们应该是模型属性,因为它们不是表单的用户输入。它在技术上有效,但并不严格遵守关注点分离。

参考/学分:动态表单域 - http://www.thymeleaf.org/doc/thymeleafspring.html#dynamic-fields

春控制器:

package net.martian111.examples.spring.web; 

import java.util.Arrays; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.ModelAttribute; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.servlet.ModelAndView; 

@Controller 
@RequestMapping("/public/stackoverflow/q26181188") 
public class StackoverflowQ26181188Controller { 

    public static class Entry { 
     private Long id; 

     private List<String> values; 

     public Long getId() { 
      return id; 
     } 

     public void setId(Long id) { 
      this.id = id; 
     } 

     public List<String> getValues() { 
      return values; 
     } 

     public void setValues(List<String> values) { 
      this.values = values; 
     } 

    } 

    public static class FormBackingBean { 
     List<Entry> entries; 

     public List<Entry> getEntries() { 
      return entries; 
     } 

     public void setEntries(List<Entry> entries) { 
      this.entries = entries; 
     } 

     public Map<Long, List<String>> getMyMap() { 
      Map<Long, List<String>> map = new HashMap<>(); 
      for (Entry entry : entries) { 
       // StringListWrapper constructed from entry.getValues() here... 
       map.put(entry.getId(), entry.getValues()); 
      } 
      return map; 
     } 

     @Override 
     public String toString() { 
      StringBuilder sb = new StringBuilder(); 
      int i = 1; 
      for (Entry entry : entries) { 
       sb.append("Pair #" + i + ": ID=" + entry.getId() + ", Values=" 
         + entry.getValues() + "\n"); 
       ++i; 
      } 
      return sb.toString(); 
     } 
    } 

    // Can be set within the @RequestMapping methods too (mv.addObject()) 
    @ModelAttribute("keys") 
    public List<Long> getKeys() { 
     return Arrays.asList(null, 1L, 2L, 3L); 
    } 

    @ModelAttribute("values") 
    public List<String> getValues() { 
     return Arrays.asList(null, "abc", "def", "ghi"); 
    } 

    @RequestMapping(method = RequestMethod.GET) 
    public ModelAndView get() { 
     ModelAndView mv = new ModelAndView("stackoverflow/q26181188"); 
     // Blank Form Backing Bean 
     FormBackingBean fbb = new FormBackingBean(); 
     fbb.setEntries(Arrays.asList(new Entry(), new Entry(), new Entry(), 
       new Entry(), new Entry())); 
     mv.addObject("fbb", fbb); 
     return mv; 
    } 

    @RequestMapping(method = RequestMethod.POST) 
    public ModelAndView post(FormBackingBean fbb) { 

     ModelAndView mv = new ModelAndView("stackoverflow/q26181188"); 
     mv.addObject("fbb", fbb); 

     // Print Form Backing Bean 
     System.out.println("FBB: \n" + fbb); 

     // Redisplay submitted from 
     return mv; 
    } 
} 

Thymeleaf模板:

<div th:each="entry,entryStat : *{entries}"> 
     Pair #<span th:text="${entryStat.count}">1</span> 
     <select th:field="*{entries[__${entryStat.index}__].id}"> 
     <option th:each="key : ${keys}" th:value="${key}" th:text="${key}"></option> 
     </select> 
     <select multiple="multiple" th:field="*{entries[__${entryStat.index}__].values}"> 
     <option th:each="value : ${values}" th:value="${value}" th:text="${value}"></option> 
     </select> 
    </div> 

    <button type="submit" name="submit" class="btn btn-primary">Submit</button> 

    </form> 
</body> 
</html> 

编辑:响应的其他细节第一公司mment MapList属性都可以映射到HTML表单字段的集合。每个Map.Entry或列表元素都映射到单个HTML表单字段,其名称格式为propertyName[index],其中index是List的情况下元素的整数索引,或者是条件的键值一个Map。上面的解决方案说明了List的情况。

为了说明Map情况下,说你要导致myMap具有以下内容的HTML表单:

123L : ["abc", "def"] 
234L : ["abc", "ghi"] 

工作倒退,对Spring MVC所需要的查询字符串(URL编码之前)创建上面的Map将需要如下所示:myMap[123]=abc&myMap[123]=def&myMap[234]=abc&myMap[234]=ghi。要让浏览器提交该查询字符串,HTML表单将必须具有两个多<select>表单元素,其中一个使用name="myMap[123]",另一个使用name="myMap[234]"。但是,表单元素的名称不能由标准HTML中的其他表单字段设置。换句话说,没有th:field值的关键<select>元素来做到这一点(回答这个Stackoverflow的问题)。

即便如此,一个开箱即用的解决方案将是客户端JavaScript脚本,它从表单字段收集必要的数据并创建提交表单所需的查询字符串。对于不同的观众来说,这将是一个不同的问题,但我认为这将是一个不必要的复杂和专业化的问题。此外,尽管上述解决方案既可以使用相同的 HTML表单生成来自MyDTO的HTML视图,也可以通过表单提交生成MyDTO,但JavaScript解决方案需要针对每个方向使用不同的专用代码。

+0

谢谢。在这里,通过创建一个名为'Entity'的新类来提供另一种使用地图的方法,该类包含'id'(类似于'myMap'的键,而'List '是值。 。但是,如果不修改架构,没有办法解决吗? – user12458 2014-10-06 11:04:33

+0

我在原始问题的末尾添加了更多细节,简短答案是“否”,原因是StackOverflow问题的参数。可能包含诸如客户端JavaScript之类的东西,但我不明白为什么这是必要的。如果'MyDTO'是整个应用程序用于其他目的的模型对象,那么'MyDTO'可能不应该用作表单-backing bean for this form。视图和控制器之间的表单支持bean存在用于特定目的,并且不应该影响应用程序其余部分的体系结构。 – 2014-10-06 23:42:45

相关问题