当前位置: 首页 > 面试题库 >

如何将对象列表与thymeleaf绑定?

颜新
2023-03-14
问题内容

我在将表单回发到控制器时遇到很多困难,该表单应该仅包含用户可以编辑的对象的数组列表。

表单可以正确加载,但是在发布时,似乎从未实际发布过任何内容。

这是我的表格:

<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>

上面的工作正常,它可以正确加载列表。但是,当我POST时,它返回一个空对象(大小为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>
...

我无法访问currentClient(编译错误),我甚至不能选择客户端列表,它给了我类似的选项get()add()clearAll()等等,所以它的东西应该有一个数组,但是,我不能在传递数组。

我也尝试过使用th:field=${},这样会导致运行时异常

我试过了

th:field = "*{clientList[__currentClient.clientID__]}" 

而且还会编译错误。

有任何想法吗?

更新1:
Tobias建议我需要将清单包装在包装盒中。这就是我所做的:

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.addAttribute("wrapper", wrapper);)

如果我随后进行选择,即勾选第一个条目:

There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='clientWithSelectionListWrapper'. Error count: 1

我猜我的POST控制器没有获取clientWithSelectionListWrapper。不知道为什么,因为我将包装对象设置为通过th:object="wrapper"FORM标头中的发布回。

更新2:
我已经取得了一些进步!最后,提交的表单将由控制器中的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并向POST方法添加了参数(不确定是否需要)。

public String processQuery(@ModelAttribute ClientWithSelectionListWrapper wrapper, BindingResult bindingResult, Model model)

因此,在发布对象时,其外观如下:

当然,应该假定systemInfo为空(在此阶段),但是clientID始终为0,而​​ipAddress / Description始终为null。尽管对于所有属性,所选的布尔值都是正确的。我确定我对某处某个属性犯了一个错误。回到调查。

更新3:
好的,我设法正确填写了所有值!但是我必须更改我的名称,td以包括不是我想要的内容…但是,这些值正确填充,这表明spring寻找的也许是数据映射的输入标签?

这是我如何更改clientID表数据的示例:

<td>
 <input type="text" readonly="readonly"                                                          
     th:name="|clientList[${stat.index}]|"
     th:value="${currentClient.getClientID()}"
     th:field="*{clientList[__${stat.index}__].clientID}"
  />
</td>

现在我需要弄清楚如何将其显示为纯数据,理想情况下不存在任何输入框…


问题答案:

你需要一个包装对象来保存提交的数据,如下所示:

public class ClientForm {
    private ArrayList<String> clientList;

    public ArrayList<String> getClientList() {
        return clientList;
    }

    public void setClientList(ArrayList<String> clientList) {
        this.clientList = clientList;
    }
}

并将其用作@ModelAttribute你的processQuery方法:

@RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(@ModelAttribute ClientForm form, Model model){
    System.out.println(form.getClientList());
}

此外,该input元素需要a name和a value。如果你直接构建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<String>。但在你的情况下ClientWithSelectionListWrapper包含ArrayList<ClientWithSelection>。因此,clientList[1]应该clientList[1].clientID与要发送回的其他属性依此类推:

<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>

我已经构建了一个小演示,因此你可以对其进行测试:

应用程序

@SpringBootApplication
public class Application {      
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }       
}

ClientWithSelection.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;
   }
}

TestController.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";
   }
}

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>


 类似资料:
  • 我很难将表单发回控制器,因为它应该只包含用户可以编辑的对象的arraylist。 表单加载正确,但在发布时,它似乎从未真正发布任何内容。 这是我的表格: 上面的工作正常,它正确加载列表。但是,当我发布时,它返回一个空对象(大小为0)。我认为这是由于缺少,但无论如何,这里有一个控制器POST方法: 我尝试添加一个,但不管我做什么,它都会导致异常。 我试过: 我不能访问当前客户端(编译错误),我甚至不

  • 我的发帖方法: 索引页表单: 当我单击submit时,browser显示为错误的请求,但没有th:field=“*{userRole}”,我可以提交表单。有什么办法解决这个问题吗? 然后如@roel所述更改了表单。 多谢了。

  • 我有一个包含可能的运输选项列表的实体: 现在,我想将带有thymeleaf的对象绑定到html表单,以便轻松创建/编辑此类实体。 但是如何插入包含所有ShippingTypes的复选框列表并将其绑定到对象?

  • 我正在尝试为我的列表模式中的每个对象创建一个包含该对象数据的表单。 所以我的控制器中有: 在我的ThymeLeaf模板中: 控制器: ThymeLeaf模板: 这种情况不会引发任何异常,但为空

  • 问题内容: 我有SourceObjects列表,我需要将其转换为ResultObjects列表。 我可以使用ResultObject的方法将一个对象获取到另一个对象: 我当然可以这样: 但对于能够展示如何使用 rxJava 进行相同操作的人,我将非常感激。 问题答案: 如果发出,则可以使用以下运算符: (将您的列表转换为可观察项) (将您的商品转换为其他商品) 运算符(将完成的Observable

  • 我希望有一个PropertyBean(由property、value和datatype组成)的多个实例来描述一个文档。因此,在Thymeleaf前端(带有Spring后端)中,一个文档应该显示为一个表,其中包含一个这样的PropertyBean实例列表。每个实例的“值”字符串应该在一个输入字段中,允许修改。(后来也是其他的,但我觉得从小开始是个好主意。 这就是我所拥有的: PropertyWrap