skip to Main Content

I’ve tried to implement TodoList editing into my GetTodoController and it doesn’t work.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Create Todo</title>
    <link rel="stylesheet" th:href="@{/styles/todo/operation.css}">
</head>
<body>
    <h2>Edit your Todo!</h2>
    <form th:method="PUT" th:action="@{/todo/edit/{id}(id=${list.id})}" th:object="${list}">
        <label for="name">Name: </label>
        <input type="text" th:field="*{name}" id="name">
        <span style="color: #ba6a65" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
        <br>
        <label for="description">Description: </label>
        <textarea th:field="*{description}" id="description">Enter text here...</textarea>
        <span style="color: #ba6a65" th:if="${#fields.hasErrors('description')}" th:errors="*{description}"></span>
        <br>
        <label for="deadline">Deadline: </label>
        <input type="datetime-local" th:field="*{deadline}" id="deadline">
        <span style="color: #ba6a65" th:if="${#fields.hasErrors('deadline')}" th:errors="*{description}"></span>
        <br>
        <input type="submit">
    </form>
</body>
</html>
@RequestMapping("/todo/edit/{id}")
@Controller
public class UpdateTodoController {
    private final TodoListRepository todoListRepository;

    @Autowired
    public UpdateTodoController(TodoListRepository todoListRepository) {
        this.todoListRepository = todoListRepository;
    }

    @ModelAttribute(name = "list")
    public TodoList todoList(@PathVariable Long id) {
        return todoListRepository.findById(id).orElse(null);
    }

    @GetMapping
    public String getUpdateTodo(@PathVariable Long id) {
        return "todo/update";
    }

    @PutMapping
    public String putUpdateTodo(@PathVariable Long id, @ModelAttribute(name = "list") @Valid TodoList todoList, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) return "todo/update";
        System.out.println(todoList);
        todoListRepository.update(todoList);
        return "redirect:/todo";
    }
}

I’ve changed @PutMapping to @PostMapping and th:method="PUT" to th:method="POST" and everything has worked, but it is conceptually wrong, cuz of @PostMapping – adding new, @PutMapping – updating

2

Answers


  1. <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Create Todo</title>
        <link rel="stylesheet" th:href="@{/styles/todo/operation.css}">
    </head>
    <body>
        <h2>Edit your Todo!</h2>
        <form method="POST" th:action="@{/todo/edit/{id}(id=${list.id})}" th:object="${list}">
            <input type="hidden" name="_method" value="PUT" />
            <label for="name">Name: </label>
            <input type="text" th:field="*{name}" id="name">
            <span style="color: #ba6a65" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
            <br>
            <label for="description">Description: </label>
            <textarea th:field="*{description}" id="description">Enter text here...</textarea>
            <span style="color: #ba6a65" th:if="${#fields.hasErrors('description')}" th:errors="*{description}"></span>
            <br>
            <label for="deadline">Deadline: </label>
            <input type="datetime-local" th:field="*{deadline}" id="deadline">
            <span style="color: #ba6a65" th:if="${#fields.hasErrors('deadline')}" th:errors="*{description}"></span>
            <br>
            <input type="submit">
        </form>
    </body>
    </html>
    
    @RequestMapping("/todo/edit/{id}")
    @Controller
    public class UpdateTodoController {
        private final TodoListRepository todoListRepository;
    
        @Autowired
        public UpdateTodoController(TodoListRepository todoListRepository) {
            this.todoListRepository = todoListRepository;
        }
    
        @ModelAttribute(name = "list")
        public TodoList todoList(@PathVariable Long id) {
            return todoListRepository.findById(id).orElse(null);
        }
    
        @GetMapping
        public String getUpdateTodo(@PathVariable Long id) {
            return "todo/update";
        }
    
        @PutMapping
        public String putUpdateTodo(@PathVariable Long id, @ModelAttribute(name = "list") @Valid TodoList todoList, BindingResult bindingResult) {
            if (bindingResult.hasErrors()) return "todo/update";
            System.out.println(todoList);
            todoListRepository.update(todoList);
            return "redirect:/todo";
        }
    }
    

    Here are the changes explained:

    1. In your HTML form, I changed th:method from PUT to POST. This is because HTML forms only support GET and POST methods directly.

    2. I added a hidden input field with the name _method and value PUT. This is a workaround provided by Spring’s HiddenHttpMethodFilter. When the form is submitted, it will be sent as a POST request, but the HiddenHttpMethodFilter will see the _method parameter and treat it as a PUT request before it reaches your controller.

    3. In your Spring controller, I kept @PutMapping. With the changes made in the HTML form, Spring’s HiddenHttpMethodFilter will ensure that this method is correctly invoked when the form is submitted.

    Remember to ensure that HiddenHttpMethodFilter is registered in your Spring Boot application. If you’re using Spring Boot, it auto-configures this filter for you.

    Login or Signup to reply.
  2. For PUT mappings to work with Spring Boot and Thymeleaf you need to set the following property in application.properties:

    spring.mvc.hiddenmethod.filter.enabled=true
    

    This will enable the hidden method filter and when used with th:method the filter will allow PUT methods to be processed correctly.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search