Skip to main content Link Search Menu Expand Document Toggle dark mode Copy Code (external link)

<template> : The Template Element for Repeating Content

The <template> element is a powerful data-binding component that generates repeating content by iterating over collections. It enables dynamic content generation, data context scoping, and complex data transformations in PDF documents.


The template element is synonymous with using the handlebars {{#each collection }} helper, as this provides a greater visibility and standardisation. However, there are some advanced options within the template component that can give greater flexibility without calculations.


On this page

Usage

The <template> element creates repeating content that:

  • Iterates over arrays, lists, and enumerable collections
  • Creates a new data context for each item
  • Supports nested templates for hierarchical data
  • Can filter and paginate data with start, step, and max count
  • Works with XML, JSON, and .NET object data sources
  • Supports conditional rendering through data binding
  • Allows inline content definition via data-content attribute
<template data-bind="{{model.items}}">
    <div class="item">
        <h3>{{.title}}</h3>
        <p>{{.description}}</p>
    </div>
</template>

template vs #each

The engine supports both the xml compliant and valid <template> element, along with the handlebars {{#each }} notation. And they each offer looping over collections of data.

Pro’s of #each

  • Simple syntax
  • Clear formatting within the template on most editors

Con’s of #each

  • Not valid XML template content.
  • Cannot specify start or step values
  • Can cause the parsing to fail completely if the inner content is not valid xml

Pro’s of

  • Maintains the validity of the xml file.
  • Offers support for data-step, data-start and data-count
  • Supports data-content for dynamic templates

Con’s of

  • Less visible in the editor as not highlighted.
  • More complex, and non-standard syntax to remember, or look up.

Supported Attributes

Data Binding Attributes

Attribute Type Description
data-bind expression Required. Binds to a collection to iterate over. Each item becomes the data context for the template content.
data-bind-start integer Zero-based index of the first item to generate. Default is 0.
data-bind-step integer Step increment for iteration. Use 2 to show every other item, 3 for every third, etc. Default is 1.
data-bind-max integer Maximum number of items to generate. Useful for pagination or limiting output. Default is unlimited.
data-content string Inline HTML content to use as the template. Overrides child elements.

Styling and Performance Attributes

Attribute Type Description
data-cache-styles boolean When true, caches styles for better performance with large datasets. Default is false.
data-style-identifier string Unique identifier for style caching across the document.

Standard HTML Attributes

Attribute Type Description
hidden string Controls visibility. Set to “hidden” to prevent template rendering.

Data Context and Binding

Current Item Reference

Within a template, use {{.}} to reference the current item, or {{.propertyName}} to access properties:

<template data-bind="{{model.users}}">
    <div>
        <strong>{{.name}}</strong> - {{.email}}
    </div>
</template>

Parent Context Access

Access parent context using path notation:

<template data-bind="{{model.orders}}">
    <div>
        Order {{.orderNumber}} for customer {{model.customerName}}
    </div>
</template>

Index and Count

The template automatically provides context about iteration:

<template data-bind="{{model.items}}">
    <div>Item {{$index + 1}} of {{$count}}: {{.name}}</div>
</template>

Notes

Template Content

The template element itself doesn’t render - only its content is generated for each iteration. The content can be:

  1. Child Elements: Standard HTML elements as children
  2. Inline Content: Using data-content attribute with HTML string
  3. Mixed Content: Text and elements combined

Data Types Supported

The data-bind attribute accepts:

  • .NET Collections: List<T>, Array, IEnumerable<T>
  • JSON Arrays: When using JSON data sources
  • XML NodeSets: When using XPath expressions
  • Data Tables: Rows from database results
  • Custom Enumerables: Any type implementing IEnumerable

Performance Optimization

For large datasets (100+ items):

  1. Enable data-cache-styles="true" to reuse style calculations
  2. Set data-style-identifier for consistent style caching
  3. Use data-bind-max to limit items per page
  4. Minimize complex expressions in binding

Nested Templates

Templates can be nested to handle hierarchical data. Each nested template creates its own data context.

Empty Collections

If the bound collection is empty or null, the template generates no output without error.


Examples

Basic Iteration

<!-- Model: { products: [{name: "Widget", price: 19.99}, {name: "Gadget", price: 29.99}] } -->
<template data-bind="{{model.products}}">
    <div style="margin-bottom: 10pt;">
        <strong>{{.name}}</strong>: ${{.price}}
    </div>
</template>

<!-- Output: -->
<!-- Widget: $19.99 -->
<!-- Gadget: $29.99 -->

Table Row Generation

<table style="width: 100%;">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Status</th>
        </tr>
    </thead>
    <tbody>
        <template data-bind="{{model.records}}">
            <tr>
                <td>{{.id}}</td>
                <td>{{.name}}</td>
                <td>{{.status}}</td>
            </tr>
        </template>
    </tbody>
</table>

Nested Templates for Hierarchical Data

<!-- Model: { departments: [{name: "Sales", employees: [{name: "John"}, {name: "Jane"}]}] } -->
<template data-bind="{{model.departments}}">
    <div style="margin-bottom: 20pt; padding: 10pt; border: 1pt solid #ccc;">
        <h2>{{.name}} Department</h2>
        <ul>
            <template data-bind="{{.employees}}">
                <li>{{.name}}</li>
            </template>
        </ul>
    </div>
</template>

<!-- Output: -->
<!-- Sales Department -->
<!--   - John -->
<!--   - Jane -->

Pagination with Start and Max

<!-- Show items 10-19 (page 2 of 10 items per page) -->
<template data-bind="{{model.allItems}}"
          data-bind-start="10"
          data-bind-max="10">
    <div class="item">{{.title}}</div>
</template>

Step Iteration (Every Other Item)

<!-- Show only odd-indexed items -->
<template data-bind="{{model.items}}"
          data-bind-start="0"
          data-bind-step="2">
    <div>{{.name}}</div>
</template>

<!-- Show only even-indexed items -->
<template data-bind="{{model.items}}"
          data-bind-start="1"
          data-bind-step="2">
    <div>{{.name}}</div>
</template>

Conditional Rendering with Expressions

<template data-bind="{{model.items}}">
    <div hidden="{{.isHidden ? 'hidden' : ''}}">
        {{.content}}
    </div>
</template>

Complex Nested Template with Totals

<!-- Invoice with line items -->
<div class="invoice">
    <h1>Invoice #{{model.invoiceNumber}}</h1>
    <p>Customer: {{model.customerName}}</p>

    <table style="width: 100%; margin: 20pt 0;">
        <thead>
            <tr style="background-color: #f0f0f0;">
                <th>Description</th>
                <th style="text-align: right;">Qty</th>
                <th style="text-align: right;">Price</th>
                <th style="text-align: right;">Total</th>
            </tr>
        </thead>
        <tbody>
            <template data-bind="{{model.lineItems}}">
                <tr>
                    <td>{{.description}}</td>
                    <td style="text-align: right;">{{.quantity}}</td>
                    <td style="text-align: right;">${{.unitPrice}}</td>
                    <td style="text-align: right;">${{.total}}</td>
                </tr>
            </template>
        </tbody>
        <tfoot>
            <tr style="font-weight: bold; border-top: 2pt solid black;">
                <td colspan="3" style="text-align: right;">Total:</td>
                <td style="text-align: right;">${{model.totalAmount}}</td>
            </tr>
        </tfoot>
    </table>
</div>

Multi-Level Nested Categories

<!-- Model: categories with subcategories and products -->
<template data-bind="{{model.categories}}">
    <div style="margin-bottom: 30pt;">
        <h2 style="color: #336699; border-bottom: 2pt solid #336699;">
            {{.categoryName}}
        </h2>

        <template data-bind="{{.subcategories}}">
            <div style="margin: 15pt 0 15pt 20pt;">
                <h3 style="color: #666;">{{.name}}</h3>

                <template data-bind="{{.products}}">
                    <div style="margin: 5pt 0 5pt 20pt; padding: 5pt; border-left: 3pt solid #ccc;">
                        <strong>{{.productName}}</strong> - ${{.price}}
                        <br/>
                        <span style="font-size: 9pt; color: #666;">{{.description}}</span>
                    </div>
                </template>
            </div>
        </template>
    </div>
</template>

Inline Content Template

<!-- Using data-content for dynamic template generation -->
<template data-bind="{{model.widgets}}"
          data-content="<div style='padding: 10pt;'><strong>{{.title}}</strong><br/>{{.description}}</div>">
</template>

Styled Cards with Alternating Colors

<style>
    .card {
        padding: 15pt;
        margin-bottom: 10pt;
        border-radius: 5pt;
    }
    .card:nth-child(odd) {
        background-color: #f9f9f9;
    }
    .card:nth-child(even) {
        background-color: #e9e9e9;
    }
</style>

<template data-bind="{{model.articles}}">
    <div class="card">
        <h3 style="margin: 0 0 5pt 0;">{{.title}}</h3>
        <p style="margin: 0; color: #666;">{{.summary}}</p>
        <div style="margin-top: 5pt; font-size: 9pt; color: #999;">
            By {{.author}} on {{.date}}
        </div>
    </div>
</template>

Directory Listing with File Types

<template data-bind="{{model.files}}">
    <div style="padding: 8pt; border-bottom: 1pt solid #ddd;">
        <div style="display: inline-block; width: 40%;">
            <img src="{{.iconUrl}}" style="width: 16pt; height: 16pt; vertical-align: middle;"/>
            <span style="margin-left: 5pt;">{{.filename}}</span>
        </div>
        <div style="display: inline-block; width: 30%;">
            {{.fileType}}
        </div>
        <div style="display: inline-block; width: 30%; text-align: right;">
            {{.fileSize}}
        </div>
    </div>
</template>

Chart Data Labels

<!-- Generating labels for a bar chart -->
<div style="position: relative; height: 300pt;">
    <template data-bind="{{model.chartData}}">
        <div style="position: absolute;
                    left: {{$index * 50}}pt;
                    bottom: 0;
                    width: 40pt;
                    height: {{.value * 2}}pt;
                    background-color: #336699;">
        </div>
        <div style="position: absolute;
                    left: {{$index * 50}}pt;
                    bottom: -20pt;
                    width: 40pt;
                    text-align: center;
                    font-size: 8pt;">
            {{.label}}
        </div>
    </template>
</div>

Timeline with Date Grouping

<template data-bind="{{model.events}}">
    <div style="margin-bottom: 15pt; padding-left: 20pt; border-left: 2pt solid #336699;">
        <div style="font-weight: bold; color: #336699; margin-bottom: 5pt;">
            {{.date}}
        </div>
        <template data-bind="{{.items}}">
            <div style="margin-bottom: 8pt; padding-left: 15pt;">
                <div style="font-weight: bold;">{{.time}} - {{.title}}</div>
                <div style="color: #666; font-size: 9pt;">{{.description}}</div>
            </div>
        </template>
    </div>
</template>

Performance-Optimized Large Dataset

<!-- Efficient rendering of 1000+ items -->
<template data-bind="{{model.largeDataset}}"
          data-cache-styles="true"
          data-style-identifier="large-list-item">
    <div class="list-item">
        {{.name}} - {{.value}}
    </div>
</template>

Conditional Sections

<!-- Only render template if collection has items -->
<div style="border: 1pt solid #ccc; padding: 10pt;">
    <h3>Available Products</h3>
    <template data-bind="{{model.products}}">
        <div class="product">
            {{.name}} - ${{.price}}
        </div>
    </template>
    <div hidden="{{model.products.length > 0 ? 'hidden' : ''}}">
        <em>No products available.</em>
    </div>
</div>

Master-Detail Report

<template data-bind="{{model.customers}}">
    <div style="page-break-before: always; padding: 20pt;">
        <!-- Customer Header -->
        <div style="background-color: #336699; color: white; padding: 10pt; margin-bottom: 15pt;">
            <h1 style="margin: 0;">{{.companyName}}</h1>
            <div>Contact: {{.contactName}} | Phone: {{.phone}}</div>
        </div>

        <!-- Customer Orders -->
        <h2>Orders</h2>
        <table style="width: 100%; margin-bottom: 20pt;">
            <thead>
                <tr style="background-color: #f0f0f0;">
                    <th>Order Date</th>
                    <th>Order #</th>
                    <th style="text-align: right;">Amount</th>
                </tr>
            </thead>
            <tbody>
                <template data-bind="{{.orders}}">
                    <tr>
                        <td>{{.orderDate}}</td>
                        <td>{{.orderNumber}}</td>
                        <td style="text-align: right;">${{.amount}}</td>
                    </tr>
                </template>
            </tbody>
        </table>

        <!-- Order Details -->
        <h2>Order Details</h2>
        <template data-bind="{{.orders}}">
            <div style="margin-bottom: 20pt; padding: 10pt; border: 1pt solid #ddd;">
                <h3>Order {{.orderNumber}} - {{.orderDate}}</h3>
                <table style="width: 100%;">
                    <thead>
                        <tr>
                            <th>Product</th>
                            <th style="text-align: right;">Qty</th>
                            <th style="text-align: right;">Price</th>
                            <th style="text-align: right;">Total</th>
                        </tr>
                    </thead>
                    <tbody>
                        <template data-bind="{{.items}}">
                            <tr>
                                <td>{{.productName}}</td>
                                <td style="text-align: right;">{{.quantity}}</td>
                                <td style="text-align: right;">${{.unitPrice}}</td>
                                <td style="text-align: right;">${{.lineTotal}}</td>
                            </tr>
                        </template>
                    </tbody>
                    <tfoot>
                        <tr style="font-weight: bold;">
                            <td colspan="3" style="text-align: right;">Order Total:</td>
                            <td style="text-align: right;">${{.amount}}</td>
                        </tr>
                    </tfoot>
                </table>
            </div>
        </template>
    </div>
</template>

See Also