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.