The "forEach" tag is a looping tag that allows looping over bean values that are Collections. This tag must contain a body. The tag's block of cells is copied and processed once for each collection element found.
There are 4 Employees, 2 of which have a manager. Notice the use of the nested if tag to conditionally display the manager name and avoid a NullPointerException.
Employee | Salary | Manager |
<jt:forEach items="${employees}" var="employee">${employee.lastName}, ${employee.firstName} | ${employee.salary} | <jt:if test="${employee.getManager() != null}" then="${employee.manager.lastName}, ${employee.manager.firstName}"/></jt:forEach> |
...gets transformed into...
Employee | Salary | Manager |
Stack, Robert | $1000.00 | |
Queue, Suzie | $900.00 | Stack, Robert |
Fudd, Elmer | $800.00 | Stack, Robert |
Bunny, Bugs | $1500.00 |
Here's an example that uses the "copyRight" attribute.
<jt:forEach items="${numberList}" var="number" copyRight="true">${number}</jt:forEach> | Shift me right! |
...gets transformed into...
3 | 23 | 100 | -10 | Shift me right! |
Here's an example that uses the "where" attribute, limiting those records displayed to those with a salary greater than or equal to 900.
Employee | Salary | Manager |
<jt:forEach items="${employees}" var="employee" where="${employee.salary >= 900}">${employee.lastName}, ${employee.firstName} | ${employee.salary} | <jt:if test="${employee.getManager() != null}" then="${employee.manager.lastName}, ${employee.manager.firstName}"/></jt:forEach> |
...gets transformed into...
Employee | Salary | Manager |
Stack, Robert | $1000.00 | |
Queue, Suzie | $900.00 | Stack, Robert |
Bunny, Bugs | $1500.00 |
Here's an example that uses the "limit" attribute, once with a limit less than the collection size, and once with a limit more than the collection size.
Employee | Salary | Manager |
<jt:forEach items="${employees}" var="employee" limit="${limit}">${employee.lastName}, ${employee.firstName} | ${employee.salary} | <jt:if test="${employee.getManager() != null}" then="${employee.manager.lastName}, ${employee.manager.firstName}"/></jt:forEach> |
Content | Below | the Block |
...if "limit" is only 2, then it gets transformed into...
Employee | Salary | Manager |
Stack, Robert | $1000.00 | |
Queue, Suzie | $900.00 | Stack, Robert |
Content | Below | the Block |
...but if "limit" is 6, then it gets transformed into...
Employee | Salary | Manager |
Stack, Robert | $1000.00 | |
Queue, Suzie | $900.00 | Stack, Robert |
Fudd, Elmer | $800.00 | Stack, Robert |
Bunny, Bugs | $1500.00 | |
Content | Below | the Block |
Here's an example that uses the "fixed" attribute. Notice how there is already room for the collection content, and that any content below the block does not get shifted.
Employee | Salary | Manager |
<jt:forEach items="${employees}" var="employee" fixed="true">${employee.lastName}, ${employee.firstName} | ${employee.salary} | <jt:if test="${employee.getManager() != null}" then="${employee.manager.lastName}, ${employee.manager.firstName}"/></jt:forEach> |
I am | not getting | moved! |
...gets transformed into...
Employee | Salary | Manager |
Stack, Robert | $1000.00 | |
Queue, Suzie | $900.00 | Stack, Robert |
Fudd, Elmer | $800.00 | Stack, Robert |
Bunny, Bugs | $1500.00 | |
I am | not getting | moved! |
Here's an example that uses the "indexVar" attribute. Notice that the variable is zero-based, and one is added so it can start with "1.".
Employee | Salary | Manager |
<jt:forEach items="${employees}" var="employee" indexVar="index">${index + 1}. ${employee.lastName}, ${employee.firstName} | ${employee.salary} | <jt:if test="${employee.getManager() != null}" then="${employee.manager.lastName}, ${employee.manager.firstName}"/></jt:forEach> |
...gets transformed into...
Employee | Salary | Manager |
1. Stack, Robert | $1000.00 | |
2. Queue, Suzie | $900.00 | Stack, Robert |
3. Fudd, Elmer | $800.00 | Stack, Robert |
4. Bunny, Bugs | $1500.00 |
Here's an example that uses the "groupBy" attribute. Notice that there are two employees in each of two departments.
<jt:forEach items="${employees}" var="dept" groupBy="deptName">Department Name: ${dept.obj.deptName} | ||
<jt:forEach items="${dept.items}" var="employee">Employee | Salary | Manager |
${employee.lastName}, ${employee.firstName} | ${employee.salary} | <jt:if test="${employee.getManager() != null}" then="${employee.manager.lastName}, ${employee.manager.firstName}"/></jt:forEach></jt:forEach> |
...gets transformed into...
Department Name: Cartoon Characters | ||
Employee | Salary | Manager |
Fudd, Elmer | $800.00 | Stack, Robert |
Bunny, Bugs | $1500.00 | |
Department Name: Data Structures Programmers | ||
Employee | Salary | Manager |
Stack, Robert | $1000.00 | |
Queue, Suzie | $900.00 | Stack, Robert |
Building on the "groupBy" example, this example adds an "orderBy" attribute. The "orderBy" attribute works well with or without a "groupBy" attribute. But if both are present, then any "orderBy" properties that are present in the "groupBy" attribute must be specified BEFORE any "orderBy" properties that are not present in the "groupBy" attribute.
<jt:forEach items="${employees}" var="dept" groupBy="deptName" orderBy="deptName desc;lastName">Department Name: ${dept.obj.deptName} | ||
<jt:forEach items="${dept.items}" var="employee">Employee | Salary | Manager |
${employee.lastName}, ${employee.firstName} | ${employee.salary} | <jt:if test="${employee.getManager() != null}" then="${employee.manager.lastName}, ${employee.manager.firstName}"/></jt:forEach></jt:forEach> |
...gets transformed into...
Department Name: Data Structures Programmers | ||
Employee | Salary | Manager |
Queue, Suzie | $900.00 | Stack, Robert |
Stack, Robert | $1000.00 | |
Department Name: Cartoon Characters | ||
Employee | Salary | Manager |
Bunny, Bugs | $1500.00 | |
Fudd, Elmer | $800.00 | Stack, Robert |