Is it possible to POST one specific item from a List using Thymeleaf?

Viewed 489

In my Spring Boot app i have the following RequestMapping:

@GetMapping("/test")
public String get(Model model) {
    List<CustomItem> items = itemService.findAll();
    model.addAttribute("items", items);
    return "test";
}

I'm displaying these items in a simple HTML table (one row for one item).

I'd like a add a button to each row that submits only the corresponding CustomItem to an endpoint something like this:

@PostMapping("/test")
public String post(CustomItem item) {
    // doing something with item
    return "redirect:/test";
}

What i've tried is to create a separate form for each row:

<table>
 <tr th:each="item, stat : ${items}">
  <td>
   <form th:object="${items[__${stat.index}__]}" th:action="@{/test}" method="post">
    <input type="text" th:field="${items[__${stat.index}__].someField}">
    <button type="submit">Submit</button>
   </form>
  </td>
 </tr>
</table>

But i'm receiving the following error when navigating to the page:

Neither BindingResult nor plain target object for bean name 'items[0]' available as request attribute

I've tried the following as well:

<table>
 <tr th:each="item, stat : ${items}">
  <td>
   <form th:object="${item}" th:action="@{/test}" method="post">
    <input type="text" th:field="*{someField}">
    <button type="submit">Submit</button>
   </form>
  </td>
 </tr>
</table>

In this case the error is the following:

Neither BindingResult nor plain target object for bean name 'item' available as request attribute

I cannot figure out what's wrong with my approach, so i'd really appreciate any advice.

EDIT:

As @StefanEmanuelsson suggested i've tried omitting the th:object attribute:

<table>
 <tr th:each="item, stat : ${items}">
  <td>
   <form th:action="@{/test}" method="post">
    <input type="text" th:field="${items[__${stat.index}__].someField}">
    <button type="submit">Submit</button>
   </form>
  </td>
 </tr>
</table>

This way the page loads just fine, but on submitting the form the value of someField in the received(?) CustomItem in the controller is null.

2 Answers

This worked for me:

Instantiate item in the controller and set to the model:

@GetMapping("/test")
public String get(Model model) {
    List<CustomItem> items = itemService.findAll();
    model.addAttribute("items", items);
    model.addAttribute("item", new CustomItem());
    return "test";
}

HTML:

<table>
        <tr th:each="i : ${items}">
            <form th:action="@{/test}" method="post" th:object="${item}">

                <td th:text="${i.id}" />
                <td th:text="${i.name}" />
            <td><input type="hidden" th:value="${i.id}" name="id" />
                <input type="hidden" th:value="${i.someField}" name="someField" />
                <button type="submit" name="action" value="remove">OK</button></td>
            </form>
        </tr>
    </table>

And create a method in controller to handle the item:

@PostMapping("/test")
    public String test(@ModelAttribute CustomItem item,HttpServletRequest request) {
        doStuff(item);
    }

I've managed to solve this problem by simply using th:value and name attributes instead of th:field:

<table>
 <tr th:each="item : ${items}">
  <td>
   <form th:action="@{/test}" method="post">
    <input type="text" th:value="${item.someField}" name="someField">
    <button type="submit">Submit</button>
   </form>
  </td>
 </tr>
</table>
Related