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 iterationcollection- 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:
- Checks if the collection contains any items
- Iterates through items if available
- 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
- Limit collection size - Filter large collections before template processing
- Avoid deep nesting - Limit nested loops to 2-3 levels for better performance
- Pre-process data - Perform complex calculations before passing data to templates
- 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>>
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.