A user can complete an item by clicking on the circle in front of the description of the todo item.
To make this work in a "classic" Thymeleaf application, we need to add a form around the item so a PUT
request can be send to the server.
Update the todoItem
fragment in fragments.html
to this:
<li th:fragment="todoItem(item)">
<div class="view">
<form th:action="@{/{id}/toggle(id=${item.id})}" th:method="put"> (1)
<input class="toggle" type="checkbox"
onchange="this.form.submit()"> (2)
<label th:text="${item.title}">Taste JavaScript</label> (3)
</form>
<button class="destroy"></button>
</div>
<input class="edit" value="Create a TodoMVC template">
</li>
1 |
form element to send a PUT request to toggle the completed state of the item |
2 |
Since we don’t have a separate submit button to submit the form, we need this little bit of JavaScript to trigger the submit as soon as the input checkbox changes. |
3 |
The <label> needs to be in the <form> as well because of the way the CSS of TodoMVC is structured, it is not really needed functionally. |
The corresponding controller code to make this work is this:
@PutMapping("/{id}/toggle") (1)
public String toggleSelection(@PathVariable("id") Long id) { (2)
TodoItem todoItem = repository.findById(id) (3)
.orElseThrow(() -> new TodoItemNotFoundException(id));
todoItem.setCompleted(!todoItem.isCompleted()); (4)
repository.save(todoItem); (5)
return "redirect:/"; (6)
}
1 |
Annotate the method with @PutMapping since we want to react to a HTTP PUT request. |
2 |
Extract the id of the item from the path. |
3 |
Use the repository to find the item in the database. |
4 |
Toggle the boolean completed state of the item. |
5 |
Save the item back to the database. |
6 |
Redirect the browser to the root page so it can display the updated todo item. |
Because we are using PUT
and browsers only really support POST
and GET
, we need to enable the HiddenHttpMethodFilter like this:
src/main/resources/application.properties
spring.mvc.hiddenmethod.filter.enabled=true
|
The HiddenHttpMethodFilter works together with Thymeleaf to allowing using PUT , DELETE , etc….
If you specify th:method="put" for example, then Thymeleaf use post as the actual method on the form and insert an extra hidden input _method with the preferred HTTP method like this:
<form action="/1/toggle" method="post">
<input type="hidden" name="_method" value="put"/>
...
</form>
On the server, this will turn into a PUT request that we can handle with a @PutMapping annotation.
|
We could test this, but we would need to check in the database to see if the completed
state was really changed.
Probably a better idea to update our application to also show the state.
If we look closely to the example HTML from TodoMVC, we can see that 2 things need to change in the generated HTML when an item is completed:
-
Add the completed
CSS class on the <li>
element
-
Add the checked
attribute to the <input>
element
We do this by using th:classappend
to add a CSS class and th:attrappend
to add an extra attribute:
<li th:fragment="todoItem(item)" th:classappend="${item.completed?'completed':''}"> (1)
<div class="view">
<form th:action="@{/{id}/toggle(id=${item.id})}" th:method="put">
<input class="toggle" type="checkbox"
onchange="this.form.submit()"
th:attrappend="checked=${item.completed?'true':null}"> (2)
<label th:text="${item.title}">Taste JavaScript</label>
</form>
<button class="destroy"></button>
</div>
<input class="edit" value="Create a TodoMVC template">
</li>
1 |
Conditionally add the completed CSS class depending on the completed attribute of the item. |
2 |
Conditionally set the checked attribute. By using null if the item is not completed, the checked attribute is not added at all to the resulting HTML. |
Start the application again and you should be able to complete the todo items: