Common Mistakes
Learn to identify and fix common data binding errors before they become problems.
Learning Objectives
By the end of this article, youβll be able to:
- Recognize common data binding errors
- Fix syntax and formatting mistakes
- Avoid null reference issues
- Handle context and scope correctly
- Debug template binding problems
- Follow best practices for reliable templates
Mistake 1: Missing or Wrong Context
β Problem: Forgetting this in Loops
{{#each products}}
<p>{{name}}</p> <!-- May not bind correctly -->
{{/each}}
β
Solution: Always Use this
{{#each products}}
<p>{{this.name}}</p> <!-- Explicit context -->
{{/each}}
Why It Matters
Without this, the binding engine may look for the property in the wrong scope, especially in nested loops or complex templates.
Mistake 2: Incorrect Parent Context Navigation
β Problem: Using ../ for Values Passed to the Document
{{#each departments}}
{{#each this.employees}}
<p>{{../../companyName}}</p> <!-- Won't work -->
{{/each}}
{{/each}}
β Solution: Use the Name Directly
Values added with WithData - such as companyName or model - are available everywhere by their name, inside any number of loops.
{{#each departments}}
{{#each this.employees}}
<p>{{companyName}}</p>
{{/each}}
{{/each}}
../ is one level only
../ reads a property of the item in the loop directly outside the current one. It goes up a single level - ../../ is not supported.
{{#each departments}}
{{#each this.employees}}
<p>{{this.name}} - {{../name}}</p> <!-- employee - department -->
{{/each}}
{{/each}}
To use a value from further out, store it in a <var> in the outer loop. See Parent (../).
Mistake 3: Case Sensitivity Issues
β Problem: Mismatched Case
// C# code
builder.WithData("model", new { Name = "John" }); // Capital N
<!-- Template -->
<p>{{model.name}}</p> <!-- lowercase n - Won't bind! -->
β Solution: Match Case Exactly
builder.WithData("model", new { name = "John" }); // lowercase n
<p>{{model.name}}</p> <!-- lowercase n - Will bind! -->
Best Practice
Use consistent casing convention:
- camelCase for properties (recommended):
firstName,orderTotal - PascalCase if matching C# classes:
FirstName,OrderTotal
Mistake 4: Null Reference Errors
β Problem: Accessing Null Properties
builder.WithData("model", new
{
customer = (object)null // Null object
});
<p>{{model.customer.name}}</p> <!-- Error! customer is null -->
β Solution: Check for Null
{{#if model.customer}}
<p>{{model.customer.name}}</p>
{{else}}
<p>No customer information</p>
{{/if}}
Or provide defaults in C#:
builder.WithData("model", new
{
customer = customerData ?? new { name = "N/A", email = "N/A" }
});
Mistake 5: Wrong Operator in Conditionals
β Problem: Using = Instead of ==
{{#if model.status = 'active'}} <!-- Assignment, not comparison! -->
<p>Active</p>
{{/if}}
β
Solution: Use == for Comparison
{{#if model.status == 'active'}} <!-- Correct comparison -->
<p>Active</p>
{{/if}}
Common Comparison Operators
==Equal to!=Not equal to>Greater than>=Greater than or equal<Less than<=Less than or equal
Mistake 6: Expression Syntax
β Problem: Expression in Quotes
{{'model.price + model.tax'}} <!-- Outputs the text "model.price + model.tax" -->
β Solution: Write the Expression Directly
{{model.price + model.tax}}
β Problem: Operator Precedence
<!-- a = 1, b = 2, c = 3 -->
{{model.a + model.b * model.c}} <!-- 7: the multiplication is done first -->
β Solution: Use Parentheses
{{(model.a + model.b) * model.c}} <!-- 9 -->
calc() is not needed inside {{ }} - the operators work directly. See Operators.
Mistake 7: Forgetting Quotes in Strings
β Problem: Unquoted String Literals
{{concat(Hello, model.name)}} <!-- Error: Hello is undefined -->
β Solution: Quote String Literals
{{concat('Hello, ', model.name)}} <!-- Correct -->
Mistake 8: Not Closing Blocks
β Problem: Missing {{/if}} or {{/each}}
{{#if model.showSection}}
<div>Content</div>
<!-- Missing {{/if}} -->
<p>More content</p>
β Solution: Always Close Blocks
{{#if model.showSection}}
<div>Content</div>
{{/if}}
<p>More content</p>
Debugging Tip
Count your opening and closing tags:
{{#if}}must have{{/if}}{{#each}}must have{{/each}}- Keep them properly nested
Mistake 9: Percentage Format Confusion
β Problem: Double Multiplication
<!-- If model.rate is already 15 (as in 15%) -->
{{format(model.rate, 'P0')}} <!-- Shows 1500%! -->
β Solution: Understand the Input
<!-- If model.rate is 0.15 (decimal) -->
{{format(model.rate, 'P0')}} <!-- Shows 15% -->
<!-- If model.rate is 15 (already percentage) -->
{{model.rate}}% <!-- Shows 15% -->
Remember: P format multiplies by 100!
Mistake 10: Modifying Parameters After Parsing
β Problem: Setting Params Too Late
var builder = factory.NewDocument().WithLayoutFile("template.html");
await builder.SaveAsync("output.pdf");
builder.WithData("model", data); // Too late!
β Solution: Set Parameters Before Saving
await factory.NewDocument()
.WithLayoutFile("template.html")
.WithData("model", data) // Set first
.SaveAsync("output.pdf"); // Then save
Mistake 11: Overwriting Parameters
β Problem: Same Key Used Twice
builder.WithData("model", customerData);
builder.WithData("model", invoiceData); // Overwrites customer data!
β Solution: Use Different Keys
builder.WithData("customer", customerData);
builder.WithData("invoice", invoiceData);
Mistake 12: Complex Inline Calculations
β Problem: Unreadable Template Logic
<p>{{format((model.price * model.quantity) + (model.price * model.quantity * 0.08), 'C2')}}</p>
β Solution: Calculate in C#
var subtotal = price * quantity;
var tax = subtotal * 0.08;
var total = subtotal + tax;
builder.WithData("model", new
{
price = price,
quantity = quantity,
subtotal = subtotal,
tax = tax,
total = total
});
<p>{{format(model.total, 'C2')}}</p> <!-- Simple and readable -->
Mistake 13: Not Handling Empty Collections
β Problem: No Fallback for Empty Data
<h2>Products</h2>
{{#each products}}
<div>{{this.name}}</div>
{{/each}}
<!-- Shows nothing if empty -->
β Solution: Add Clause
<h2>Products</h2>
{{#each products}}
<div>{{this.name}}</div>
{{else}}
<p>No products available.</p>
{{/each}}
Mistake 14: Incorrect Date Formatting
β Problem: Wrong Format Specifiers
{{format(model.date, 'YYYY-MM-DD')}} <!-- Wrong: YYYY should be yyyy -->
β Solution: Use Correct Specifiers
{{format(model.date, 'yyyy-MM-dd')}} <!-- Correct: lowercase yyyy -->
Common Mistakes:
YYYYβ Should beyyyyDDβ Should beddmm(minutes) vsMM(month)
Mistake 15: Ignoring Data Types
β Problem: Treating Numbers as Strings
builder.WithData("model", new
{
price = "123.45" // String, not number
});
{{model.price * 2}} <!-- May not work correctly -->
β Solution: Use Correct Data Types
builder.WithData("model", new
{
price = 123.45 // Numeric type
});
{{model.price * 2}} <!-- Works correctly -->
Debugging Checklist
When data binding doesnβt work, check:
- Case sensitivity - Property names match exactly?
- Context - Using
thisin loops? - Null values - Checking for null before accessing properties?
- Closed blocks - Every
{{#if}}has{{/if}}? - Data added -
WithDatacalled with the name the template uses? - Data types - Numbers as numbers, not strings?
- Format strings - Using correct format specifiers?
- Operators - Using
==for comparison, not=?
Best Practices Summary
Doβs β
- Always use
thisin loops - Check for null before accessing nested properties
- Calculate complex values in C# code
- Use consistent naming conventions
- Provide fallbacks for empty collections
- Set parameters early before saving
- Use clear variable names in templates
- Test with empty/null data to catch edge cases
Donβts β
- Donβt put complex logic in templates
- Donβt ignore null values
- Donβt forget to close blocks
- Donβt use
=for comparisons (use==) - Donβt calculate everything in templates
- Donβt rely on default type conversions
- Donβt nest loops too deeply (> 3 levels)
- Donβt forget to quote string literals
Testing Your Templates
Create Test Data
public object CreateTestData()
{
return new
{
// Normal cases
customer = new { name = "John Doe", email = "john@example.com" },
// Edge cases
emptyList = new object[] { },
nullValue = (string)null,
largeNumber = 1234567890,
smallDecimal = 0.001,
date = DateTime.Now,
// Boundary cases
longText = new string('A', 1000),
specialChars = "Test & <special> characters"
};
}
Test Edge Cases
[Test]
public async Task TestTemplateWithEmptyData()
{
var result = await _factory.NewDocument()
.WithLayoutFile("template.html")
.WithData("model", new
{
items = new object[] { } // Empty collection
})
.BuildAsync();
Assert.IsTrue(result.Success, result.Error);
Assert.Greater(result.Document.Binary.Length, 0);
}
[Test]
public async Task TestTemplateWithNullValues()
{
var result = await _factory.NewDocument()
.WithLayoutFile("template.html")
.WithData("model", new
{
optionalField = (string)null
})
.BuildAsync();
// Should still generate
Assert.IsTrue(result.Success, result.Error);
}
Quick Reference: Common Fixes
| Error | Fix |
|---|---|
| Property not binding | Check case sensitivity |
| Null reference | Add {{#if}} check |
| Wrong parent context | Use names directly for document values; ../ is one level only |
| Wrong calculation result | Add parentheses - * and / are done before + and - |
| Percentage shows wrong | Check if value needs P format |
| Block not rendering | Check for closing {{/if}} or {{/each}} |
| Data not appearing | Check the WithData name matches the template |
| Empty collection shows nothing | Add {{else}} clause |
Next Steps
Now that you know how to avoid common mistakes:
- Styling PDFs - Apply advanced styling
- Layout Techniques - Master page layout
- Practical Applications - Build real-world documents
Continue learning β Styling PDFs**