Pass an instance of an object in each loop from thymeleaf html file to Spring controller

Himitsu :

I have a problem with passing an instance of "threads" in each loop from thymeleaf to Spring controller using a submit button. I'm trying to solve this by using the annotation @ModelAttribute, but one more instance of MessageThread is creating.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org" xmlns:form="http://www.w3.org/1999/html">
<head>
    <meta charset="UTF-8">
    <title>Форум</title>
</head>
    <body>

        <form method="post">
            <input type="text" name="header">
            <input type="text" name="text">
            <button name="newThread" type="submit">Создать тред</button>
        </form>

        <table>
            <thead>
            <tr>
                <th> Тред </th>
                <th> ОП пост </th>
            </tr>
            </thead>
            <tbody>
            <tr th:if="${threads.isEmpty()}">
                <td colspan="2"> Нет доступных тредов </td>
            </tr>
            <div>
                <th:block th:each="thread : ${threads}">
                    <td th:type="id"><span th:text="${thread.getId()}"></span></td>
                    <td><span th:text="${thread.getHeader()}"> Title </span></td>
                    <td><span th:text="${thread.getText()}"> Title </span></td>
                    <form th:object="${thread}" th:method="post">
                        <td><button name="inThread" type="submit">В тред</button></td>
                    </form>
                </th:block>
            </div>
            </tbody>
        </table>

    </body>
</html>

I can't find a way to pass an instance of "threads" from thymeleaf. All that I want is to press Submit button and pass the ${thread} to "toThread" method.

My controller:

@Controller
public class ThreadController {

private final MessageService messageService;

@Autowired
public ThreadController(MessageService messageService) {
    this.messageService = messageService;
}

@GetMapping("/")
public String showThreads(Model model)
{
    model.addAttribute("threads", messageService.getThreads());
    return "threads-view";
}

@PostMapping(value = "/", params = "newThread")
public String addThread(Model model,
                        @RequestParam("header") String header,
                        @RequestParam("text") String text)
{
    model.addAttribute("threads", messageService.getThreads());
    messageService.addThread(header, text);
    return "redirect:/";
}

@PostMapping(value = "/", params = "inThread")
public String toThread(@ModelAttribute("thread") MessageThread thread) {
    System.out.println(thread.getId() + " " + thread.getHeader());
    return "redirect:/thread:";
}

}

MessageThread class:

package com.project.imageboard.model;

 import java.util.ArrayList;
 import java.util.List;

 public class MessageThread {

private String header;
private String text;
private int id;

private List<Message> messages = new ArrayList<>();

public MessageThread(String header, String text) {
    this.header = header;
    messages.add(new Message(text));
    this.text = text;
    this.id = messages.get(0).getId();
}

public int getId() {
    return id;
}
public String getText() {
    return text;
}
public String getHeader() {
    return header;
}

public List<Message> getMessages() {
    return messages;
}

public void insertMessage(Message message){
    messages.add(message);
}

}

I would be grateful for any help.

Daniel Camarda :

your controller seems to be ok, you are mapping a post request to "/" and expecting to receive a MessageThread object which has to be built from the content of the request, the problem is in the template: you are not sending the data in the request from the client to the server, so spring has no way to assign the correct values to a new instance of MessageThread.

If we take this block from your template:

<th:block th:each="thread : ${threads}">
    <td th:type="id"><span th:text="${thread.getId()}"></span></td>
    <td><span th:text="${thread.getHeader()}"> Title </span></td>
    <td><span th:text="${thread.getText()}"> Title </span></td>
    <form th:object="${thread}" th:method="post">
        <td><button name="inThread" type="submit">В тред</button></td>
    </form>
</th:block>

1) The form tag is missing the action attribute, try adding something like th:action="@{/}" to target your request mapping on the server side.

2) You are not sending the actual content of the thread object to the server, for this you have to add input tags with the name of the imput matching the name of the field in the MessageThread object you want to populate. Something like the following:

<input type="hidden" th:field="*{header}" />
<input type="hidden" th:field="*{text}" />

In this example th:field is creating the name and value attributes for the input tags so you don't need to do it manually.

To sum up, there is no way to pass an actual "instance" of an object from the html running in the client to the java app running on the server, you can only send data using HTTP and parse that data on the server. The ModelAttribute annotation is instructing Spring to inspect an object (MessageThread in this case) and find in the data sent through the request the matching values to populate the object.

Hope this helps.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=359171&siteId=1