Search code examples
htmlspring-bootthymeleaf

How to access each arraylist within arraylist with thymeleaf?


There are Three classes

public Class Port{

private String portname;
// with getters and setters
}

public Class Application{
private String appName;
private List<Port> ports=  new ArrayList<Port>();
//  with getters and setters
}

public Class Service{
private String serviceName;
private List<Application> apps=  new ArrayList<Application>();
//  with getters and setters
}

Below snippet is part of the Thymeleaf HTML code to iterate through the fields.

<form action="#" th:action="@{/processWrapper}" th:object="${service}" method="post">
<table>
<div th:each="app, stat : *{apps}">
<tr>                   
<td><input type="text" th:field="*{apps[__${stat.index}__].appName}" th:name="|apps[${stat.index}]|" /></td>
<div th:each="port, stat1 : *{app.ports}">
<td><input type="text" th:field="*{app.ports[__${stat1.index}__].portname}" th:name="|app.ports[${stat1.index}]|" /></td>
    </div>
    </div></table></form>

Why is it not working?I get the error message:

Property or field 'ports' cannot be found on object of type 'service' maybe not public?


Solution

  • Your html should look like this:

    <form action="#" th:action="@{/processWrapper}" th:object="${service}" method="post">
        <table>
            <tr th:each="app, stat : *{apps}">                   
                <td><input type="text" th:field="*{apps[__${stat.index}__].appName}" /></td>
                <td th:each="port, stat1 : ${app.ports}"><input type="text" th:field="*{apps[__${stat.index}__].ports[__${stat1.index}__].portname}" /></td>
            </tr>
        </table>
    </form>
    

    As for what was wrong...

    1. You don't need all those extra divs. Just do the th:each on the trs and tds themselves.
    2. You don't need th:name when you use th:field. th:field generates the name property.
    3. This expression is invalid: *{app.ports[__${stat1.index}__].portname}.
      • First, you can't use *{} expressions on local variables. *{app} is invalid -- it's trying to resolve to ${service.app} which doesn't exist.
      • Second when you are building a th:field expressions, you have to build the entire path. The corrected expression is *{apps[__${stat.index}__].ports[__${stat1.index}__].portname}, which includes the entire path to portname.