Monday, April 30, 2018

How to iterate over a List or Map Object in Thymeleaf (With Examples)?

Although Thymeleaf provides an easy way to iterate over List or Map objects but if one doesn't know the exact syntax for iteration then it could be tricky and one might get surprising results. Today we would discuss how to iterate over a List or Map object in Thymeleaf using th:each attribute.


Table Of Content
1. Introduction
2. Iterate Over List Object
3. Iterate Over Map Object
4. Keeping Iteration Status
5. Conclusion


1. Introduction


One could use Thymeleaf's provided th:each attribute to iterate over List or Map objects. One could iterate below objects using th:each attribute:
  • Any object implementing java.util.Iterable
  • Any object implementing java.util.Map
  • Any array
  • Any other object will be treated as if it were a single-valued list containing the object itself.

2. Iterate Over List Object


Let's understand it by an example. Suppose we have a small Product Search Utility that's implemented by Thymeleaf. Here, products is the model attribute that contains the Product List object.
<h2>Product Search</h2>
<div style="margin: 30px;">
<form th:action="@{/product-search}">
<section>
<label> 
<input type="text" id="searchbox" name="searchbox" placeholder="enter product ID / Product Label" required style="width:500px;" minlength=3>
</label> <input type="submit" value="Search" id="submit">
</section>

<div th:if="${not #lists.isEmpty(products)}">
<h3>Search Results</h3>
<table>
<thead><tr><th>Product ID</th><th>Product Label</th><th>Product Description</th></tr></thead>
<th:block th:each="product : ${products}">
<tr>
<td><a th:href="@{/productdetails(id=${product.id})}" th:text="${product.id}">Product ID..</a></td>
<td th:text="${product.label}">Product Label..</td>
<td th:text="${product.description}">Product Description..</td>
</tr>
</th:block>
</table>
</div>
<div th:if="${#lists.isEmpty(products)}">
<h3>Search Results</h3>
Try Again!! Using some other Product ID or Product Label
</div>
</form>
</div>
</div>

3. Iterate Over Map Object


Again let's take an example. Suppose we would like to iterate over a Map Object something like
  • Map<Integer, String> reportMap: Suppose we have populated a Map object while uploading a CSV file. The Key here is the row number in the CSV file and Value is the string containing message in the format <Success|Failed - Reason for Success or Failure of Upload >
    <div th:if="${not #lists.isEmpty(reportMap)}">
       <table>
     <tr><td>Row No</td><td>Status</td><td>Message</td></tr>
     <th:block th:each="row : ${reportMap}">
     <tr>
        <td th:text="${row.key}">Row No</td>
        <td th:text="${#strings.listSplit(row.value,'-')[0]}">Status</td>
        <td th:text="${#strings.listSplit(row.value,'-')[1]}">Message</td>
     </tr>
     </th:block>
       </table>
    </div>
    
    As you can see, the syntax for iterating over a Map object is exactly similar to iterating over a List object. The only difference is, how are we accessing Key and it's corresponding Values. 
  • Map<String, List<String>> stateToDistrictMap: Suppose we have a Map object which have State as it's Key and List of districts in a State as it's Value. Then how would we iterate over this kind of Map object.
    <div th:if="${not #lists.isEmpty(stateToDistrictMap)}">
       <table>
         <tr><td>State</td><td>Districts</td></tr>
     <th:block th:each="state : ${stateToDistrictMap}">
       <tr>
         <td th:text="${state.key}">State</td>
      <th:block th:each="district : ${state.value}">
        <td th:text="${district}">District</td>
      </th:block>
       </tr>
     </th:block>
         </table>
    </div>
    
    Note: Please turn a blind eye to the table structure. Here objective is to show you how to iterate & access values inside a Map object. 

4. Keeping iteration Status


One could keep track of their iteration using the status variable. The only constraint is that the status variable will only be available inside the fragment of code defined by the tag holding the th:each attribute. Status variables contain the following data:
  • The current iteration index, starting with 0. This is the index property.
  • The current iteration index, starting with 1. This is the count property.
  • The total amount of elements in the iterated variable. This is the size property.
  • The iter variable for each iteration. This is the current property.
  • Whether the current iteration is even or odd. These are the even/odd boolean properties.
  • Whether the current iteration is the first one. This is the first boolean property.
  • Whether the current iteration is the last one. This is the last boolean property.
Let's take an example.
<table>
<thead><tr><th>Product ID</th><th>Product Label</th><th>Product Description</th></tr></thead>
<tr th:each="product,iterStat : ${products}" th:class="${iterStat.odd}? 'odd'">
<td><a th:href="@{/productdetails(id=${product.id})}" th:text="${product.id}">Product ID..</a></td>
<td th:text="${product.label}">Product Label..</td>
<td th:text="${product.description}">Product Description..</td>
</tr>
</table>
If one don’t explicitly set a status variable then Thymeleaf will always create one for us by suffixing Stat to the name of the iteration variable:
<table>
<thead><tr><th>Product ID</th><th>Product Label</th><th>Product Description</th></tr></thead>
<tr th:each="product : ${products}" th:class="${productStat.odd}? 'odd'"><td><a th:href="@{/productdetails(id=${product.id})}" th:text="${product.id}">Product ID..</a></td>
<td th:text="${product.label}">Product Label..</td>
<td th:text="${product.description}">Product Description..</td>
</tr>
</table>

5. Conclusion


Now we know how to iterate and access values inside a List or Map Object in Thymeleaf. Although the iteration mechanism is same for all the objects whether it's List or Map but accessing values inside a List or Map is slightly different.
  • For List : Iteration variable would directly give the value inside a List whereas 
  • For Map: {iteration_variable.key} would give the Map's key and {iteration_variable.value} would give the corresponding value of the Key. If the {iteration_variable.value} is a List then again one could use th:each attribute to iterate over this List as per the requirement.
Hoping that, now one could easily iterate over a List or Map objects in Thymeleaf. Happy Learning!!  If you have any suggestions or questions do let me know.


4 comments:

  1. 3.b.) List is not coming on new line ,Can you please help ???

    ReplyDelete
    Replies
    1. List is not coming on new line- It means although the list are getting populated but it's values are not coming in new line. Depending upon your requirement, you can use br tag or use ul/ol or use CSS.

      Delete
  2. Amigos necesito una solución para enviar la información de un table al Controller que se envié como una List.

    ReplyDelete
    Replies
    1. Using Google Translator, I got your query i.e. "Friends I need a solution to send the information of a table to the Controller that was sent as a List.".
      I understand that, you would like to send the information of a table to the Controller.
      Assumption: On Click of a button you would like to send the table information.
      Solution: On click event, gather table data using Javascript/jQuery and then use AXIOS(promise based HTTP client) POST to send this data to controller. You have to use @RequestBody annotation in the controller layer.

      Delete