Skip to main content

Repeat Paragraphs - Foreach Loop Guide

Documents frequently contain repeating content structures such as lists of items, multiple records, or repeated sections with similar formatting. PDF4me's template engine provides powerful iteration capabilities to generate multiple paragraphs and sections dynamically from data collections.


Understanding Foreach Loops

The foreach loop is the primary mechanism for iterating through data collections and generating repeated content. Each iteration processes one item from the collection and generates corresponding output.

Basic Foreach Syntax

<<foreach [item in collection]>>
Content with <<[item.property]>>
<</foreach>>

Components:

  • item - Variable representing the current element in the iteration
  • collection - The data array or collection to iterate through
  • Content block - Template content repeated for each item

Repeating Paragraphs

Generate multiple paragraphs by iterating through a data collection and creating formatted text for each item.

Example: Application Ratings

Template Syntax:

<<foreach [app in applications]>><<[app.listAppName]>> in <<[app.listAppType]>> platform is a <<[app.category]>> app and has a <<[app.AppRating]>> rating.
<</foreach>>

Data Source (JSON):

{
"surveyName": "Top-rated Apps",
"applications": [
{
"listAppName": "Instagram",
"listAppType": "iOS",
"category": "Social Media",
"AppRating": "4.9"
},
{
"listAppName": "Whatsapp",
"listAppType": "iOS",
"category": "Chat",
"AppRating": "4.8"
},
{
"listAppName": "Player UG",
"listAppType": "Android",
"category": "Gaming",
"AppRating": "4.75"
}
]
}

Generated Output:

Instagram in iOS platform is a Social Media app and has a 4.9 rating.
Whatsapp in iOS platform is a Chat app and has a 4.8 rating.
Player UG in Android platform is a Gaming app and has a 4.75 rating.

Handling Empty Collections

When a collection might be empty, use the Any() method to check for data before iterating. This prevents null exceptions and allows you to display alternative content.

Basic Null Check

Template with Any() Check:

<<if [applications.Any()]>>
<<foreach [app in applications]>><<[app.listAppName]>> in <<[app.listAppType]>> platform is a <<[app.category]>> app and has a <<[app.AppRating]>> rating.
<</foreach>>
<<else>>No applications listed
<</if>>

This approach:

  1. Checks if the collection contains any items
  2. Iterates through items if available
  3. Displays fallback message if collection is empty

Nested Foreach Loops

Create hierarchical structures by nesting foreach loops within each other. This is useful for displaying parent-child relationships or multi-level data structures.

Example: Department and Employees

Template:

<<foreach [dept in departments]>>
Department: <<[dept.name]>>
<<foreach [emp in dept.employees]>>
- <<[emp.name]>> (<<[emp.position]>>)
<</foreach>>
<</foreach>>

Data:

{
"departments": [
{
"name": "Engineering",
"employees": [
{ "name": "John Smith", "position": "Senior Developer" },
{ "name": "Jane Doe", "position": "Tech Lead" }
]
},
{
"name": "Marketing",
"employees": [
{ "name": "Mike Johnson", "position": "Marketing Manager" }
]
}
]
}

Repeating Complex Sections

Foreach loops can repeat entire sections of content including multiple paragraphs, formatting, and nested structures.

Multi-Paragraph Repetition

Template:

<<foreach [product in products]>>
Product Name: <<[product.name]>>
Description: <<[product.description]>>
Price: $<<[product.price]>>
Available: <<[product.inStock]>>

<</foreach>>

This template generates a complete product description block for each product, maintaining all formatting and paragraph breaks.


Conditional Iteration

Combine foreach loops with conditional statements for selective content generation.

Example: Conditional Display

Template:

<<foreach [employee in employees]>>
<<if [employee.age > 50]>>
Senior Employee: <<[employee.name]>> - Age: <<[employee.age]>>
<</if>>
<</foreach>>

Alternatively, use enumeration methods:

Template:

<<foreach [employee in employees.Where(e => e.age > 50)]>>
Senior Employee: <<[employee.name]>> - Age: <<[employee.age]>>
<</foreach>>

Formatting Within Loops

Apply formatting to fields within foreach loops for consistent output styling.

Template:

<<foreach [customer in customers]>>
Customer: <<[customer.name]:caps>>
Last Visit: <<[customer.lastVisit]:"MMMM dd, yyyy">>
Status: <<[customer.status]:upper>>

<</foreach>>

Loop Variables and Indexing

Access loop position information using implicit variables (if supported by your template engine version).

Common Loop Variables

  • Current item index (0-based or 1-based depending on implementation)
  • Loop iteration count
  • First/last item indicators

Example Usage (if supported):

<<foreach [item in items]>>
<<[$index + 1]>>. <<[item.name]>>
<</foreach>>

Performance Considerations

Best Practices for Loops

  1. Limit collection size - Filter large collections before template processing
  2. Avoid deep nesting - Limit nested loops to 2-3 levels for better performance
  3. Pre-process data - Perform complex calculations before passing data to templates
  4. Use efficient queries - Filter and sort data at the data source level

Example: Pre-filtered Data

Instead of:

<<foreach [emp in employees.Where(e => e.age > 30).OrderBy(e => e.name)]>>

Pre-filter in your application and pass filtered data:

<<foreach [emp in filteredEmployees]>>

Common Patterns

Pattern 1: Numbered List

<<foreach [item in items]>>
<<[$index]>>. <<[item.title]>>
<</foreach>>

Pattern 2: Grouped Content

<<foreach [category in categories]>>
## <<[category.name]>>

<<foreach [item in category.items]>>
- <<[item.name]>>
<</foreach>>
<</foreach>>

Pattern 3: Summary Section

<<foreach [report in reports]>>
Report: <<[report.title]>>
Total Records: <<[report.records.Count()]>>
Average Value: <<[report.records.Average(r => r.value)]>>
<</foreach>>

Troubleshooting

Common Issues

Issue: Null reference errors Solution: Use Any() to check collections before iteration

Issue: Unwanted line breaks or spacing Solution: Carefully manage whitespace in your template

Issue: Performance problems with large collections Solution: Pre-filter data and limit collection sizes

Issue: Incorrect data binding Solution: Verify property names match your data structure exactly


Practical Example: Invoice Line Items

Complete Template:

Invoice #<<[invoiceNumber]>>
Date: <<[invoiceDate]:"dd.MM.yyyy">>

Items:
<<if [lineItems.Any()]>>
<<foreach [item in lineItems]>>
<<[item.name]:caps>> - Qty: <<[item.quantity]>> - Price: $<<[item.price]:"F2">> - Total: $<<[item.quantity * item.price]:"F2">>
<</foreach>>

Subtotal: $<<[lineItems.Sum(i => i.quantity * i.price)]:"F2">>
<<else>>
No items in this invoice.
<</if>>

Complex Iterations

For complex iteration scenarios, consider pre-processing your data in your application logic before passing it to the template. This improves template readability and performance.