richersoon
richersoon

Reputation: 4902

Thymeleaf - How to loop a list by index

How can I loop by index?

Foo.java

public Foo {
    private List<String> tasks;
    ...
}

index.html

<p>Tasks:
    <span th:each="${index: #numbers.sequence(0, ${foo.tasks.length})}">
        <span th:text="${foo.tasks[index]}"></span>
    </span>
</p>

I got parse error

org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as each: "${index: #numbers.sequence(0,  ${student.tasks.length})}"

Upvotes: 57

Views: 128788

Answers (3)

user22739131
user22739131

Reputation:

First of all, please excuse my poor English.

Please be aware that, in my experience, the status variable ([1]), regarding index and count values, DOES not keep track of the status of the iteration, but returns the position of the current object in the list you're looping for.

For example. You have a list called pairs of 6 pairs like this

    0: [id=10, fk=2]
    1: [id=20, fk=2]
    2: [id=30, fk=2]
    3: [id=40, fk=1]
    4: [id=50, fk=1]
    5: [id=60, fk=1]

and you want to print those who have fk=1 BEFORE those who have fk=2 (it's an example, sorting is not important, here).

So, if you use a th_each="p:${pairs}" followed by a th:if="p.id == 1", you'll obtain

    [id=40, fk=1]
    [id=50, fk=1]
    [id=60, fk=1]
    (...)

as expected. But, what are you expecting from pStat.index and pStat.count values? I guess

    (0,1)
    (1,2)
    (2,3)

but (please verify using a <pre>([[${pStat.index}]], [[${pStat.count}]])</pre>), you'll obtain

    (3,4)
    (4,5)
    (5,6)

that is to say, the position of these pairs in the list, not the status of the iteraction itself. This could be fine for you, but be aware that, for example, if you try to keep a count on how much times you looped extracting data from a list o hundred objects (for example to insert a <br /> every ten values) results can surprise you, if the sequence of the list is not the sequence in which you'll use it.

If this is your case, a way to obtain this is extracting sublist using Collection Selection ([2]) in conjunction with Thymeleaf's #lists.toList() as in <th:block th:with="sublist=${#pairs.toList(list.?[fk ==1])}">.

This way you'll obtain

    0: [id=40, fk=1]
    1: [id=50, fk=1]
    2: [id=60, fk=1]

and your first pair will have index 0 and count 1.

Maybe this is a bug of the current verison of thymeleaf I'm using, or is not so clearly stated in documentation, but this is what I discovered staring at few code lines unexpectedly behaving (and some debugging).

Thymeleaf Iterations [1]: https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#keeping-iteration-status

SpEL Collection Selection [2]: https://docs.spring.io/spring-framework/reference/core/expressions/language-ref/collection-selection.html

Upvotes: 0

naXa stands with Ukraine
naXa stands with Ukraine

Reputation: 37916

Thymeleaf always declares implicit iteration status variable if we omit it.

<span th:each="task : ${foo.tasks}">
    <span th:text="${taskStat.index} + ': ' + ${task.name}"></span>
</span>

Here, the status variable name is taskStat which is the aggregation of variable task and the suffix Stat.

Then in the loop, we can refer to taskStat.index, taskStat.size, taskStat.count, taskStat.even and taskStat.odd, taskStat.first and taskStat.last.

Source: Tutorial: Using Thymeleaf - 6.2 Keeping iteration status

Upvotes: 52

Jim Garrison
Jim Garrison

Reputation: 86774

Thymeleaf th:each allows you to declare an iteration status variable

<span th:each="task,iter : ${foo.tasks}">

Then in the loop you can refer to iter.index and iter.size.

See Tutorial: Using Thymeleaf - 6.2 Keeping iteration status.

Upvotes: 136

Related Questions