Tag Loop Listeners

For finer control over the content and formatting of the resultant spreadsheet cells, JETT allows the user to create TagLoopListener objects that are notified every time a looping tag's block for a loop iteration has been processed. Looping tags that are not processed do not notify their TagLoopListener object. This may occur, for example, if a looping tag processes an empty collection and its block is removed. Any object that implements the TagLoopListener interface may be supplied in the onLoopProcessed attribute of any Tag that subclasses BaseLoopTag, as 3 JETT built-in tags do. The TagLoopListener interface contains two methods:

public void beforeTagLoopProcessed(TagLoopEvent event);
public void onTagLoopProcessed(TagLoopEvent event);
            

Specify a TagLoopListener object or a fully-qualified TagLoopListener class name with a looping tag. If a fully-qualified class name is supplied, then that class must have a public, no-argument constructor.

<jt:forEach items="${employees}" var="${employee}" onLoopProcessed="${myTagLoopListener}"/>
            

OR

<jt:forEach items="${employees}" var="${employee}" onLoopProcessed="com.mycompany.myproject.CustomTagLoopListener"/>
            

Before an iteration of a looping tag is processed, if it has a TagLoopListener, then it creates a TagLoopEvent and calls its TagLoopListener's beforeTagLoopProcessed method. If it returns true, then the loop iteration is processed as normal. If it returns false, then the loop iteration is not processed.

When a loop iteration of a looping tag is processed, if it has a TagLoopListener, then it creates a TagLoopEvent and calls its TagLoopListener's onTagLoopProcessed method.

A TagLoopListener can retrieve context information by retrieving properties of the TagLoopEvent. A TagLoopEvent is a TagEvent, so it inherits all of TagEvent's properties.

Sheet sheet = event.getSheet();
Map<String, Object> beans = event.getBeans();
Block block = event.getBlock();
int left = block.getLeftColNum();
int right = block.getRightColNum();
int top = block.getTopRowNum();
int bottom = block.getBottomRowNum();
int index = event.getLoopIndex();
            

The Sheet is a reference to the POI Sheet object representing the actual sheet in the spreadsheet. The Map object is a reference to the beans map. It may have extra beans added, if the Tag is a "forEach" tag, or for that matter, any tag that adds values to the beans map. The Block is an object that represents the block of Cells affected by this tag. It has properties for the left and right column numbers (0-based), and the top and bottom row numbers (0-based). The Sheet object can be used to get POI Row objects, and POI Row objects can be used to get POI Cell objects, which can then be manipulated. It can also be used to obtain the POI Workbook object. The loop index is a 0-based "looping variable" that indicates on which iteration the tag is.

Example

A TagLoopListener can be created to affect the formatting of data within a blocks of cells in a looping tag.

This TagLoopListener will shade alternating rows light gray. It uses POI objects and functionality to change the color of the alternating rows. It uses the looping index to determine whether it should color that particular iteration's block's cells light gray.

public void onTagLoopProcessed(TagLoopEvent event)
{
   Sheet sheet = event.getSheet();
   Workbook workbook = sheet.getWorkbook();
   Block block = event.getBlock();
   int left = block.getLeftColNum();
   int right = block.getRightColNum();
   int top = block.getTopRowNum();
   int bottom = block.getBottomRowNum();
   int index = event.getLoopIndex();

   if (index % 2 == 0)
   {
      for (int r = top; r <= bottom; r++)
      {
         Row row = sheet.getRow(r);
         for (int c = left; c <= right; c++)
         {
            Cell cell = row.getCell(c);
            CellStyle style = cell.getCellStyle();
            CellStyle newStyle = workbook.createCellStyle();
            newStyle.cloneStyleFrom(style);
            newStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
            newStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
            cell.setCellStyle(newStyle);
         }
      }
   }
}
            

This example uses a forEach tag to process the "employees" collection.

Employee Salary Manager
<jt:forEach items="${employees}" var="employee" onLoopProcessed="${myTagLoopListener}">${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