我很难将表单发回控制器,因为它应该只包含用户可以编辑的对象的arraylist。
表单加载正确,但在发布时,它似乎从未真正发布任何内容。
这是我的表格:
<form action="#" th:action="@{/query/submitQuery}" th:object="${clientList}" method="post">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Select</th>
<th>Client ID</th>
<th>IP Addresss</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr th:each="currentClient, stat : ${clientList}">
<td><input type="checkbox" th:checked="${currentClient.selected}" /></td>
<td th:text="${currentClient.getClientID()}" ></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}" ></td>
</tr>
</tbody>
</table>
<button type="submit" value="submit" class="btn btn-success">Submit</button>
</form>
上面的工作正常,它正确加载列表。但是,当我发布时,它返回一个空对象(大小为0)。我认为这是由于缺少th:field
,但无论如何,这里有一个控制器POST方法:
...
private List<ClientWithSelection> allClientsWithSelection = new ArrayList<ClientWithSelection>();
//GET method
...
model.addAttribute("clientList", allClientsWithSelection)
....
//POST method
@RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(@ModelAttribute(value="clientList") ArrayList clientList, Model model){
//clientList== 0 in size
...
}
我尝试添加一个th:field
,但不管我做什么,它都会导致异常。
我试过:
...
<tr th:each="currentClient, stat : ${clientList}">
<td><input type="checkbox" th:checked="${currentClient.selected}" th:field="*{}" /></td>
<td th th:field="*{currentClient.selected}" ></td>
...
我不能访问当前客户端(编译错误),我甚至不能选择clientList,它给我像get()
,add()
,clearAll()
等选项,所以它应该有一个数组,但是,我不能传入一个数组。
我还尝试过使用类似于th:field=${}
,这会导致运行时异常
我试过了
th:field = "*{clientList[__currentClient.clientID__]}"
而且还会编译错误。
有什么想法吗?
托比亚斯建议我用包装纸把我的清单包装起来。我就是这么做的:
ClientWithSelectionWrapper:
public class ClientWithSelectionListWrapper {
private ArrayList<ClientWithSelection> clientList;
public List<ClientWithSelection> getClientList(){
return clientList;
}
public void setClientList(ArrayList<ClientWithSelection> clients){
this.clientList = clients;
}
}
<form action="#" th:action="@{/query/submitQuery}" th:object="${wrapper}" method="post">
....
<tr th:each="currentClient, stat : ${wrapper.clientList}">
<td th:text="${stat}"></td>
<td>
<input type="checkbox"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:checked="${currentClient.selected}" />
</td>
<td th:text="${currentClient.getClientID()}" ></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}" ></td>
</tr>
然后我的控制器:
@RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(@ModelAttribute ClientWithSelectionListWrapper wrapper, Model model){
...
}
页面加载正确,数据按预期显示。如果我在没有任何选择的情况下发布表格,我会得到以下信息:
org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'clientList' cannot be found on null
不知道它为什么抱怨
(在GET方法中,它有:model.add属性(包装器,包装器);
)
如果我选择,即勾选第一个条目:
There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='clientWithSelectionListWrapper'. Error count: 1
我猜我的POST管理员没有用SelectionListWrapper获取客户。不知道为什么,因为我已经将包装器对象设置为通过表单标题中的th:object=“wrapper”
发回。
我取得了一些进步!最后,通过控制器中的POST方法提取提交的表单。但是,除了是否勾选了项目之外,所有属性似乎都为空。我做了各种各样的改变,看起来是这样的:
<form action="#" th:action="@{/query/submitQuery}" th:object="${wrapper}" method="post">
....
<tr th:each="currentClient, stat : ${clientList}">
<td th:text="${stat}"></td>
<td>
<input type="checkbox"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:checked="${currentClient.selected}"
th:field="*{clientList[__${stat.index}__].selected}">
</td>
<td th:text="${currentClient.getClientID()}"
th:field="*{clientList[__${stat.index}__].clientID}"
th:value="${currentClient.getClientID()}"
></td>
<td th:text="${currentClient.getIpAddress()}"
th:field="*{clientList[__${stat.index}__].ipAddress}"
th:value="${currentClient.getIpAddress()}"
></td>
<td th:text="${currentClient.getDescription()}"
th:field="*{clientList[__${stat.index}__].description}"
th:value="${currentClient.getDescription()}"
></td>
</tr>
我还向包装类添加了一个默认的无参数构造函数,并添加了一个bindingResult
param to POST方法(不确定是否需要)。
public String processQuery(@ModelAttribute ClientWithSelectionListWrapper wrapper, BindingResult bindingResult, Model model)
当然,systemInfo应该为null(在这个阶段),但clientID始终为0,ipAddress/Description始终为null。不过,对于所有属性,选定的布尔值都是正确的。我肯定我在某个地方的一处房产上犯了个错误。回到调查。
好的,我已经把所有的值都填好了!但我不得不更改我的td
以包含
以下是我如何更改clientID表数据的示例:
<td>
<input type="text" readonly="readonly"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:field="*{clientList[__${stat.index}__].clientID}"
/>
</td>
现在我需要弄清楚如何将其显示为普通数据,理想情况下不需要任何输入框。。。
匿名用户
当你想要在thymeleaf中选择对象时,你实际上不需要创建一个包装器来存储一个布尔
选择字段。根据thymeleaf指南使用动态字段
,语法th: field="*{rows[__${rowStat.index}__]。综艺}"
非常适合当你想访问集合中已经存在的一组对象时。它不是真正设计用来通过使用包装对象来进行选择的,因为它创建了不必要的样板代码,有点像黑客。
考虑这个简单的例子,一个<代码>人>代码>可以选择<代码>饮料< /代码>。注:为了清晰起见,省略了构造函数、getter和setter。此外,这些对象通常存储在数据库中,但我使用内存数组来解释这个概念。
public class Person {
private Long id;
private List<Drink> drinks;
}
public class Drink {
private Long id;
private String name;
}
Spring控制器
这里的主要内容是,我们将Person
存储在模型中
,以便将其绑定到th:object
中的表单。其次,selectableDrinks
是用户可以在UI上选择的饮料。
@GetMapping("/drinks")
public String getDrinks(Model model) {
Person person = new Person(30L);
// ud normally get these from the database.
List<Drink> selectableDrinks = Arrays.asList(
new Drink(1L, "coke"),
new Drink(2L, "fanta"),
new Drink(3L, "sprite")
);
model.addAttribute("person", person);
model.addAttribute("selectableDrinks", selectableDrinks);
return "templates/drinks";
}
@PostMapping("/drinks")
public String postDrinks(@ModelAttribute("person") Person person) {
// person.drinks will contain only the selected drinks
System.out.println(person);
return "templates/drinks";
}
模板代码
密切关注li
循环,以及如何使用selectableDrinks
获取所有可能的可选择饮料。
复选框th: field实际上扩展到person.drinks
,因为th:对象绑定到Person
和*{饮料}
只是引用Person
对象上的属性的快捷方式。您可以将此视为告诉Spring/thymeleaf,任何选定的饮料
都将被放入位置person.drinks
的ArrayList
中。
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" >
<body>
<div class="ui top attached segment">
<div class="ui top attached label">Drink demo</div>
<form class="ui form" th:action="@{/drinks}" method="post" th:object="${person}">
<ul>
<li th:each="drink : ${selectableDrinks}">
<div class="ui checkbox">
<input type="checkbox" th:field="*{drinks}" th:value="${drink.id}">
<label th:text="${drink.name}"></label>
</div>
</li>
</ul>
<div class="field">
<button class="ui button" type="submit">Submit</button>
</div>
</form>
</div>
</body>
</html>
无论如何。。。秘密酱汁使用的是th:value=${drinks.id}
。这依赖于Spring转换器。当表单发布后,spring将尝试重新创建一个人
,要做到这一点,它需要知道如何转换任何选定的饮料。id
字符串转换为实际的饮料
类型。注意:如果你做了th:value${drinks}
html复选框中的value
键将是toString()
表示的Drink
不是你想要的,因此需要使用id!。如果您正在跟随,那么您所需要做的就是创建自己的转换器(如果尚未创建)。
如果没有转换器,您将收到一个错误,如无法转换'java'类型的属性值。lang.String“to required type”java。util。酒店“饮料”列表
您可以打开登录应用程序。属性以详细查看错误<代码>日志记录。数量组织。springframework。web=跟踪
这只是意味着spring不知道如何转换表示饮料的字符串id。id
变成了饮料
。下面是修复此问题的转换器的示例。通常,您会在get access数据库中插入一个存储库。
@Component
public class DrinkConverter implements Converter<String, Drink> {
@Override
public Drink convert(String id) {
System.out.println("Trying to convert id=" + id + " into a drink");
int parsedId = Integer.parseInt(id);
List<Drink> selectableDrinks = Arrays.asList(
new Drink(1L, "coke"),
new Drink(2L, "fanta"),
new Drink(3L, "sprite")
);
int index = parsedId - 1;
return selectableDrinks.get(index);
}
}
如果一个实体有一个对应的spring数据存储库,spring会自动创建转换器,并在提供id时处理获取实体的操作(字符串id似乎也很好,所以spring会根据外观进行一些额外的转换)。这真的很酷,但一开始可能会让人困惑。
您需要一个包装器对象来保存提交的数据,如下所示:
public class ClientForm {
private ArrayList<String> clientList;
public ArrayList<String> getClientList() {
return clientList;
}
public void setClientList(ArrayList<String> clientList) {
this.clientList = clientList;
}
}
并将其用作processQuery
方法中的@ModelAttribute
:
@RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(@ModelAttribute ClientForm form, Model model){
System.out.println(form.getClientList());
}
此外,输入
元素需要一个名称
和一个值
。如果直接构建html,请考虑名称必须是clientList[i]
,其中i
是列表中项目的位置:
<tr th:each="currentClient, stat : ${clientList}">
<td><input type="checkbox"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:checked="${currentClient.selected}" />
</td>
<td th:text="${currentClient.getClientID()}" ></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}" ></td>
</tr>
请注意,clientList
可以在中间位置包含null
。例如,如果发布的数据是:
clientList[1] = 'B'
clientList[3] = 'D'
生成的ArrayList
将是:[null,B,null,D]
更新1:
在我上面的例子中,ClientForm
是List的包装器
<tr th:each="currentClient, stat : ${wrapper.clientList}">
<td><input type="checkbox" th:name="|clientList[${stat.index}].clientID|"
th:value="${currentClient.getClientID()}" th:checked="${currentClient.selected}" /></td>
<td th:text="${currentClient.getClientID()}"></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}"></td>
</tr>
我制作了一个小演示,您可以进行测试:
应用JAVA
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
客户支持选举。JAVA
public class ClientWithSelection {
private Boolean selected;
private String clientID;
private String ipAddress;
private String description;
public ClientWithSelection() {
}
public ClientWithSelection(Boolean selected, String clientID, String ipAddress, String description) {
super();
this.selected = selected;
this.clientID = clientID;
this.ipAddress = ipAddress;
this.description = description;
}
/* Getters and setters ... */
}
ClientWithSelectionListWrapper。JAVA
public class ClientWithSelectionListWrapper {
private ArrayList<ClientWithSelection> clientList;
public ArrayList<ClientWithSelection> getClientList() {
return clientList;
}
public void setClientList(ArrayList<ClientWithSelection> clients) {
this.clientList = clients;
}
}
测试控制器。JAVA
@Controller
class TestController {
private ArrayList<ClientWithSelection> allClientsWithSelection = new ArrayList<ClientWithSelection>();
public TestController() {
/* Dummy data */
allClientsWithSelection.add(new ClientWithSelection(false, "1", "192.168.0.10", "Client A"));
allClientsWithSelection.add(new ClientWithSelection(false, "2", "192.168.0.11", "Client B"));
allClientsWithSelection.add(new ClientWithSelection(false, "3", "192.168.0.12", "Client C"));
allClientsWithSelection.add(new ClientWithSelection(false, "4", "192.168.0.13", "Client D"));
}
@RequestMapping("/")
String index(Model model) {
ClientWithSelectionListWrapper wrapper = new ClientWithSelectionListWrapper();
wrapper.setClientList(allClientsWithSelection);
model.addAttribute("wrapper", wrapper);
return "test";
}
@RequestMapping(value = "/query/submitQuery", method = RequestMethod.POST)
public String processQuery(@ModelAttribute ClientWithSelectionListWrapper wrapper, Model model) {
System.out.println(wrapper.getClientList() != null ? wrapper.getClientList().size() : "null list");
System.out.println("--");
model.addAttribute("wrapper", wrapper);
return "test";
}
}
测验html
<!DOCTYPE html>
<html>
<head></head>
<body>
<form action="#" th:action="@{/query/submitQuery}" th:object="${wrapper}" method="post">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Select</th>
<th>Client ID</th>
<th>IP Addresss</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr th:each="currentClient, stat : ${wrapper.clientList}">
<td><input type="checkbox" th:name="|clientList[${stat.index}].clientID|"
th:value="${currentClient.getClientID()}" th:checked="${currentClient.selected}" /></td>
<td th:text="${currentClient.getClientID()}"></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}"></td>
</tr>
</tbody>
</table>
<button type="submit" value="submit" class="btn btn-success">Submit</button>
</form>
</body>
</html>
更新1。B:
下面是使用
th: field
并将所有其他属性作为隐藏值发送回来的相同示例。
<tbody>
<tr th:each="currentClient, stat : *{clientList}">
<td>
<input type="checkbox" th:field="*{clientList[__${stat.index}__].selected}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].clientID}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].ipAddress}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].description}" />
</td>
<td th:text="${currentClient.getClientID()}"></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}"></td>
</tr>
</tbody>
问题内容: 我在将表单回发到控制器时遇到很多困难,该表单应该仅包含用户可以编辑的对象的数组列表。 表单可以正确加载,但是在发布时,似乎从未实际发布过任何内容。 这是我的表格: 上面的工作正常,它可以正确加载列表。但是,当我POST时,它返回一个空对象(大小为0)。我相信这是由于缺少造成的,但是无论如何这里是控制器POST方法: 我尝试添加一个,但是无论我做什么,都会导致异常。 我试过了: 我无法访
我的发帖方法: 索引页表单: 当我单击submit时,browser显示为错误的请求,但没有th:field=“*{userRole}”,我可以提交表单。有什么办法解决这个问题吗? 然后如@roel所述更改了表单。 多谢了。
我正在使用spring boot(2.0.3)和Thymeleaf(3)。我在尝试将th:字段与LocalDate(Java8)绑定时遇到了一个问题。我的问题很复杂,因为日期输入是由日、月和年的单独字段分隔的。 通过temporals(thymeleaf-extras-java8time)显示localDate是可以的,但是绑定到字段是一个问题。
我正在尝试将文本字段绑定到对象。我做了一些研究,找到了这个答案。 然后,您可以像这样绑定街道地址: 我将Street实例化为什么值(在最后一行代码中)? 以上是我找到的例子。我的代码如下——我有一个联系类: 然后我有一个phoneType类: 然后在联系人编辑器中,我试图将phoneType绑定到文本字段: 在联系人编辑器中,包含在**中的行(即 binder.bind(电话类型,联系人.getP
我想保存对象(产品)与列表对象(属性)和有问题,因为我需要使用输入文本的对象属性。 产品实体 属性实体 我在百里香的表格。 我如何无法输入好的保存对象产品?控制器
我正在使用Guice开发一个小型web框架。我有一个Router对象,一旦初始化,它就会公开一个getControllerClasses()方法。我必须循环所有这些动态返回的类,以使用Guice绑定它们。 我绑定路由器: 但是,我如何在一个模块中获得绑定的路由器实例,以便也可以绑定其getControllerClasses()方法返回的类? 我能够在模块中获取路由器实例的唯一方法是,将该实例绑定到