Working with Tables
This page covers table-oriented reporting patterns used in production templates.
It explains row data bands, column data bands, and cross (pivot) table layouts.
Working with Table-Row Data Bands
A table-row data band repeats a full table row for each item in a collection.
Place the opening and closing foreach tags in the same row, then bind cell content to the current item.
Template (conceptual row inside a table):
| <<foreach [segment in booking.Segments.Where(s => s.IsConfirmed)]>> <<[segment.Carrier]>> | <<[segment.From]>> | <<[segment.To]>> | <<[segment.DepartureUtc]:"dd MMM HH:mm">><</foreach>> |Expected output:
| Pacific Air | Seattle | Tokyo | 14 Jun 09:20 |
| Pacific Air | Tokyo | Singapore | 18 Jun 13:05 |Use this pattern for manifests, schedules, invoice lines, and any one-record-per-row layout.
Working with Table-Column Data Bands
A table-column data band repeats a column for each item in a collection. This is useful when entities should expand horizontally.
Use the horizontal foreach switch and keep the repeated content in one template column.
Template (conceptual table):
| Traveler | <<foreach [traveler in booking.Travelers] -horz>><<[traveler.FullName]>><</foreach>> |
| Seat | <<foreach [traveler in booking.Travelers] -horz>><<[traveler.Seat ?? "pending"]>><</foreach>> |Expected output:
| Traveler | Avery Chen | Noah Patel |
| Seat | 12A | pending |Use this pattern when the number of compared entities is small and fixed-width horizontal growth is acceptable.
Working with Cross (Pivot) Tables
Cross (pivot) tables combine a row axis and a column axis to summarize many records compactly.
In templates, this is usually modeled by:
- generating one header column per key on the horizontal axis
- repeating body rows for the vertical axis
- resolving each body cell from filtered data
Conceptual pattern:
<<var [segmentsDates = booking.Segments.Select(s => s.DepartureUtc.ToString("yyyy-MM-dd")).Distinct().OrderBy(x => x)]>>
| Date | <<foreach [m in segmentsDates] -horz>> <<[m]>><</foreach>> |
| <<foreach [carrier in booking.Segments.Select(s => s.Carrier).Distinct().OrderBy(x => x)]>><<[carrier]>> | <<foreach [m in segmentsDates] -horz>> <<[booking.Segments.Count(s => s.Carrier == carrier && s.DepartureUtc.ToString("yyyy-MM-dd") == m)]>><</foreach>><</foreach>> |Expected output with the shared sample data:
| Date | 2026-06-14 | 2026-06-18 | 2026-06-21 |
| Harbor Jet | 0 | 0 | 1 |
| Pacific Air | 1 | 1 | 0 |For large pivots, precompute row keys, column keys, and cell aggregates in application code, then bind a pre-shaped view model.