Thymeleaf and lists in forms

Posted by Tobias on April 8, 2013 00:00 CEST Updated on April 10, 2013 22:18 CEST 8 Comments Add a comment

Contents

Thymeleaf is great. Previously I have worked a lot with JSP and Facelets and even Razor which is the one view engine in Microsoft MVC. My favourite so far is Thymeleaf even though I really enjoyed working with Razor. Recently I needed to create a view to edit multiple instances of a model directly. So I created a view which rendered a form and a table based on a model which consisted of a List. Now I needed to make Thymeleaf understand that my th:field actually was a property in an model instance in the list. This wasn't very easy. However, a forum reply by Daniel Fernandez who is the creator of Thymeleaf lead me on the right path.

According to Mr. Fernandez I had to use a somewhat funny looking syntax which would look something like this:

<div th:each="userAddress, stat : *{userAddresses}">
...
<input type="text" th:field="*{userAddresses[__${stat.index}__].userStreet}" />
...
</div>

I assumed that userAddresses is a list of something so I tried to do the same thing but with a list of PersonViewModel:

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class PersonViewModel {
	
	@Size(min=1,max=255)
	@NotNull
	private String name;

	@Size(min=1,max=255)
	@NotNull
	private String address;

	// Getters and setters omitted
}

I could however not get this to work. Instead I received an error message stating

Field or property 'model' cannot be found on object of type 'java.util.ArrayList'

The solution I eventually came up with was this:

The view:

<form action="#" th:object="${model}" th:action="@{/people/edit/}" method="post">
<ul id="validation-messages" th:if="${#fields.hasErrors('*')}">
<li th:each="err : ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li>
</ul>
<table>
<caption>People</caption>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Address</th>
<th></th>
</tr>
</thead>

<tbody>
<tr th:each="person, rowStat : *{personList}" th:class="${rowStat.even ? 'even' : 'odd'}">
<td><input type="text" th:field="*{personList[__${rowStat.index}__].id}" readonly="readonly" /></td>
<td><input type="text" th:field="*{personList[__${rowStat.index}__].name}" /></td>
<td><input type="text" th:field="*{personList[__${rowStat.index}__].address}" /></td>
<td><a th:href="@{/people/delete/} + *{personList[__${rowStat.index}__].id} + '/'">Delete</a></td>
</tr>
</tbody>

<tfoot></tfoot>
</table>
<input type="submit" value="Save" />
</form>

List container model:

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

import javax.validation.Valid;

public class PersonListFormViewModel {

@Valid
private List<PersonViewModel> personList = new ArrayList<PersonViewModel>();

public PersonListFormViewModel() {}

public PersonListFormViewModel(List<PersonViewModel> personList) {
this.personList = personList;
}

public List getPersonList() {
return personList;
}

public void setPersonList(List<PersonViewModel> personList) {
this.personList = personList;
}
}

Basically I created a wrapper class for the List and supplied the view with that instead of the list directly.

One thing to note about having a view which enables edits of more than one entity at once, if you have service methods which looks something like this:

public void save(PersonViewModel person) {
	person.setUpdatedBy(session.getCurrentUser());
	personDao.save(person);
}

public void save(PersonFormViewModel personList) {
	for (PersonViewModel person : personList) {
		save(person);
	}
}

Now, if a user edits one of the instances of PersonViewModel using the multi edit view it will look like the user updated all of the instances.

Post your comment

8 Comments (newest first)

Posted by hi on March 8, 2017 10:46 CET

hi

Posted by zammad on February 24, 2017 13:45 CET

test

Posted by Edgar Arakelyan on August 4, 2016 23:24 CEST

Great! You saved me after 2 hours of vain google search. Finally got rid of that stupid problem :)

Posted by hghg454354 on April 15, 2016 10:20 CEST

dgdfgfd

Posted by ddd on March 4, 2016 13:33 CET

ddd

Posted by krystofurr on November 17, 2015 02:47 CET

I would also like to see the controller and how it's handled when it returns to the request mapping. Great posting btw!

Posted by Andy Brave on August 26, 2015 09:30 CEST

Hi, I have the same problem but I can't understand it. Can you show me your controller? or How do you send the equest? Thanks!

Posted by fg on February 11, 2014 17:29 CET

fgfg

Read More