# Handlebars.NET ## Overview Handlebars.NET is a .NET implementation of the Handlebars.js templating engine. It provides a logic-less template syntax with `{{expression}}` for output and `{{#helper}}` for block constructs. Templates are compiled into delegates for fast repeated rendering. Handlebars.NET supports helpers (custom functions callable from templates), partials (reusable template fragments), and block helpers (custom block-level constructs). It is compatible with Mustache templates and extends them with helpers and more expressive iteration. Install via NuGet: ``` dotnet add package Handlebars.Net ``` ## Basic Template Compilation and Rendering Compile a template string into a reusable delegate, then invoke it with a data object. ```csharp using HandlebarsDotNet; // Compile once, render many times var template = Handlebars.Compile("Hello, {{name}}! You have {{count}} notifications."); var result = template(new { name = "Alice", count = 7 }); // result: "Hello, Alice! You have 7 notifications." // With nested objects var orderTemplate = Handlebars.Compile( "Order #{{order.id}} placed by {{order.customer.name}} on {{order.date}}"); var output = orderTemplate(new { order = new { id = 1042, customer = new { name = "Bob Smith" }, date = "2025-01-15" } }); ``` ## Iteration and Conditionals Use `{{#each}}` for collections and `{{#if}}`/`{{#unless}}` for conditionals. ```csharp using HandlebarsDotNet; var reportTemplate = Handlebars.Compile(@"
| Product | Price | Qty |
|---|---|---|
| {{this.name}} | ${{this.price}} | {{this.quantity}} |
No items found.
{{/if}}Generated: {{generatedAt}}
"); var html = reportTemplate(new { title = "Inventory Report", hasItems = true, items = new[] { new { name = "Widget A", price = 12.50, quantity = 100 }, new { name = "Widget B", price = 8.75, quantity = 250 }, new { name = "Gadget C", price = 45.00, quantity = 30 } }, generatedAt = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") }); ``` ## Custom Helpers Register helper functions that can be called from templates. Helpers receive the writer, context, and parameters. ```csharp using System; using System.IO; using HandlebarsDotNet; // Inline helper (returns a value) Handlebars.RegisterHelper("uppercase", (writer, context, parameters) => { writer.WriteSafeString(parameters[0]?.ToString()?.ToUpperInvariant() ?? string.Empty); }); // Formatting helper Handlebars.RegisterHelper("currency", (writer, context, parameters) => { if (parameters.Length > 0 && decimal.TryParse(parameters[0]?.ToString(), out var amount)) { var symbol = parameters.Length > 1 ? parameters[1]?.ToString() : "$"; writer.WriteSafeString($"{symbol}{amount:N2}"); } }); // Date formatting helper Handlebars.RegisterHelper("formatDate", (writer, context, parameters) => { if (parameters.Length > 0 && DateTime.TryParse(parameters[0]?.ToString(), out var date)) { var format = parameters.Length > 1 ? parameters[1]?.ToString() : "yyyy-MM-dd"; writer.WriteSafeString(date.ToString(format)); } }); var template = Handlebars.Compile( "{{uppercase name}} owes {{currency balance}} as of {{formatDate dueDate \"MMMM dd, yyyy\"}}"); var output = template(new { name = "alice", balance = 1234.50m, dueDate = "2025-06-15" }); // Output: "ALICE owes $1,234.50 as of June 15, 2025" ``` ## Block Helpers Block helpers wrap a section of template content and can conditionally render the block or iterate over it. ```csharp using HandlebarsDotNet; // Conditional block helper Handlebars.RegisterHelper("ifEqual", (output, options, context, arguments) => { if (arguments.Length >= 2 && arguments[0]?.ToString() == arguments[1]?.ToString()) { options.Template(output, context); } else { options.Inverse(output, context); } }); var template = Handlebars.Compile(@" {{#ifEqual status ""active""}} Active {{else}} Inactive {{/ifEqual}} "); var html = template(new { status = "active" }); ``` ## Partials (Reusable Fragments) Register reusable template fragments as partials and include them with `{{> partialName}}`. ```csharp using HandlebarsDotNet; // Register partials Handlebars.RegisterTemplate("header", @"