![]() |
VOOZH | about |
dotnet add package Handlebars.Net --version 2.1.6
NuGet\Install-Package Handlebars.Net -Version 2.1.6
<PackageReference Include="Handlebars.Net" Version="2.1.6" />
<PackageVersion Include="Handlebars.Net" Version="2.1.6" />Directory.Packages.props
<PackageReference Include="Handlebars.Net" />Project file
paket add Handlebars.Net --version 2.1.6
#r "nuget: Handlebars.Net, 2.1.6"
#:package Handlebars.Net@2.1.6
#addin nuget:?package=Handlebars.Net&version=2.1.6Install as a Cake Addin
#tool nuget:?package=Handlebars.Net&version=2.1.6Install as a Cake Tool
π Quality Gate Status
π Reliability Rating
π Security Rating
π Bugs
π Code Smells
π Coverage
π Stack Exchange questions
π GitHub issues questions
π GitHub issues help wanted
Blistering-fast Handlebars.js templates in your .NET application.
Handlebars.js is an extension to the Mustache templating language created by Chris Wanstrath. Handlebars.js and Mustache are both logicless templating languages that keep the view and the code separated like we all know they should be.
Check out the handlebars.js documentation for how to write Handlebars templates.
Handlebars.Net doesn't use a scripting engine to run a Javascript library - it compiles Handlebars templates directly to IL bytecode. It also mimics the JS library's API as closely as possible.
dotnet add package Handlebars.Net
The following projects are extending Handlebars.Net:
System.Text.Json.JsonDocument support)Newtonsoft.Json support)string source =
@"<div class=""entry"">
<h1>{{title}}</h1>
<div class=""body"">
{{body}}
</div>
</div>";
var template = Handlebars.Compile(source);
var data = new {
title = "My new post",
body = "This is my first post!"
};
var result = template(data);
/* Would render:
<div class="entry">
<h1>My New Post</h1>
<div class="body">
This is my first post!
</div>
</div>
*/
string source =
@"<h2>Names</h2>
{{#names}}
{{> user}}
{{/names}}";
string partialSource =
@"<strong>{{name}}</strong>";
Handlebars.RegisterTemplate("user", partialSource);
var template = Handlebars.Compile(source);
var data = new {
names = new [] {
new {
name = "Karen"
},
new {
name = "Jon"
}
}
};
var result = template(data);
/* Would render:
<h2>Names</h2>
<strong>Karen</strong>
<strong>Jon</strong>
*/
Handlebars.RegisterHelper("link_to", (writer, context, parameters) =>
{
writer.WriteSafeString($"<a href='{context["url"]}'>{context["text"]}</a>");
});
string source = @"Click here: {{link_to}}";
var template = Handlebars.Compile(source);
var data = new {
url = "https://github.com/rexm/handlebars.net",
text = "Handlebars.Net"
};
var result = template(data);
/* Would render:
Click here: <a href='https://github.com/rexm/handlebars.net'>Handlebars.Net</a>
*/
This will expect your views to be in the /Views folder like so:
Views\layout.hbs |<--shared as in \Views
Views\partials\somepartial.hbs <--shared as in \Views\partials
Views\{Controller}\{Action}.hbs
Views\{Controller}\{Action}\partials\somepartial.hbs
Handlebars.RegisterHelper("StringEqualityBlockHelper", (output, options, context, arguments) =>
{
if (arguments.Length != 2)
{
throw new HandlebarsException("{{#StringEqualityBlockHelper}} helper must have exactly two arguments");
}
var left = arguments.At<string>(0);
var right = arguments[1] as string;
if (left == right) options.Template(output, context);
else options.Inverse(output, context);
});
var animals = new Dictionary<string, string>()
{
{"Fluffy", "cat" },
{"Fido", "dog" },
{"Chewy", "hamster" }
};
var template = "{{#each this}}The animal, {{@key}}, {{#StringEqualityBlockHelper @value 'dog'}}is a dog{{else}}is not a dog{{/StringEqualityBlockHelper}}.\r\n{{/each}}";
var compiledTemplate = Handlebars.Compile(template);
string templateOutput = compiledTemplate(animals);
/* Would render
The animal, Fluffy, is not a dog.
The animal, Fido, is a dog.
The animal, Chewy, is not a dog.
*/
[Fact]
public void BasicDecorator(IHandlebars handlebars)
{
string source = "{{#block @value-from-decorator}}{{*decorator 42}}{{@value}}{{/block}}";
var handlebars = Handlebars.Create();
handlebars.RegisterHelper("block", (output, options, context, arguments) =>
{
options.Data.CreateProperty("value", arguments[0], out _);
options.Template(output, context);
});
handlebars.RegisterDecorator("decorator",
(TemplateDelegate function, in DecoratorOptions options, in Context context, in Arguments arguments) =>
{
options.Data.CreateProperty("value-from-decorator", arguments[0], out _);
});
var template = handlebars.Compile(source);
var result = template(null);
Assert.Equal("42", result);
}
For more examples see DecoratorTests.cs
In case you need to apply custom value formatting (e.g. DateTime) you can use IFormatter and IFormatterProvider interfaces:
public sealed class CustomDateTimeFormatter : IFormatter, IFormatterProvider
{
private readonly string _format;
public CustomDateTimeFormatter(string format) => _format = format;
public void Format<T>(T value, in EncodedTextWriter writer)
{
if(!(value is DateTime dateTime))
throw new ArgumentException("supposed to be DateTime");
writer.Write($"{dateTime.ToString(_format)}");
}
public bool TryCreateFormatter(Type type, out IFormatter formatter)
{
if (type != typeof(DateTime))
{
formatter = null;
return false;
}
formatter = this;
return true;
}
}
[Fact]
public void DateTimeFormatter(IHandlebars handlebars)
{
var source = "{{now}}";
var format = "d";
var formatter = new CustomDateTimeFormatter(format);
handlebars.Configuration.FormatterProviders.Add(formatter);
var template = handlebars.Compile(source);
var data = new
{
now = DateTime.Now
};
var result = template(data);
Assert.Equal(data.now.ToString(format), result);
}
By default Handlebars will create standalone copy of environment for each compiled template. This is done in order to eliminate a chance of altering behavior of one template from inside of other one.
Unfortunately, in case runtime has a lot of compiled templates (regardless of the template size) it may have significant memory footprint. This can be solved by using SharedEnvironment.
Templates compiled in SharedEnvironment will share the same configuration.
Only runtime configuration properties can be changed after the shared environment has been created. Changes to Configuration.CompileTimeConfiguration and other compile-time properties will have no effect.
[Fact]
public void BasicSharedEnvironment()
{
var handlebars = Handlebars.CreateSharedEnvironment();
handlebars.RegisterHelper("registerLateHelper",
(in EncodedTextWriter writer, in HelperOptions options, in Context context, in Arguments arguments) =>
{
var configuration = options.Frame
.GetType()
.GetProperty("Configuration", BindingFlags.Instance | BindingFlags.NonPublic)?
.GetValue(options.Frame) as ICompiledHandlebarsConfiguration;
var helpers = configuration?.Helpers;
const string name = "lateHelper";
if (helpers?.TryGetValue(name, out var @ref) ?? false)
{
@ref.Value = new DelegateReturnHelperDescriptor(name, (c, a) => 42);
}
});
var _0_template = "{{registerLateHelper}}";
var _0 = handlebars.Compile(_0_template);
var _1_template = "{{lateHelper}}";
var _1 = handlebars.Compile(_1_template);
var result = _1(null);
Assert.Equal("", result); // `lateHelper` is not registered yet
_0(null);
result = _1(null);
Assert.Equal("42", result);
}
Compatibility feature toggles defines a set of settings responsible for controlling compilation/rendering behavior. Each of those settings would enable certain feature that would break compatibility with canonical Handlebars.
By default all toggles are set to false.
Compile-time: takes affect at the time of template compilationRuntime: takes affect at the time of template renderingRelaxedHelperNamingIf true enables support for Handlebars.Net helper naming rules.
This enables helper names to be not-valid Handlebars identifiers (e.g. {{ one.two }}).
Such naming is not supported in Handlebarsjs and would break compatibility.
Compile-time[Fact]
public void HelperWithDotSeparatedName()
{
var source = "{{ one.two }}";
var handlebars = Handlebars.Create();
handlebars.Configuration.Compatibility.RelaxedHelperNaming = true;
handlebars.RegisterHelper("one.two", (context, arguments) => 42);
var template = handlebars.Compile(source);
var actual = template(null);
Assert.Equal("42", actual);
}
Used to switch between the legacy Handlebars.Net and the canonical Handlebars rules (or a custom implementation).
For Handlebars.Net 2.x.x HtmlEncoderLegacy is the default.
HtmlEncoder
Implements the canonical Handlebars rules.
HtmlEncoderLegacy
Will not encode:
= (equals)
` (backtick)
' (single quote)
Will encode non-ascii characters οΏ½, οΏ½, ...
Into HTML entities (<, â, ß, ...).
Runtime[Fact]
public void UseCanonicalHtmlEncodingRules()
{
var handlebars = Handlebars.Create();
handlebars.Configuration.TextEncoder = new HtmlEncoder();
var source = "{{Text}}";
var value = new { Text = "< οΏ½" };
var template = handlebars.Compile(source);
var actual = template(value);
Assert.Equal("< οΏ½", actual);
}
Compared to rendering, compiling is a fairly intensive process. While both are still measured in millseconds, compilation accounts for the most of that time by far. So, it is generally ideal to compile once and cache the resulting function to be re-used for the life of your process.
Nearly all time spent in rendering is in the routine that resolves values against the model. Different types of objects have different performance characteristics when used as models.
IDictionary<string, object> (microseconds).ICustomTypeDescriptor) that are not optimized for heavy reflection.TBD
Pull requests are welcome! The guidelines are pretty straightforward:
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 is compatible. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp1.0 netcoreapp1.0 was computed. netcoreapp1.1 netcoreapp1.1 was computed. netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard1.3 netstandard1.3 is compatible. netstandard1.4 netstandard1.4 was computed. netstandard1.5 netstandard1.5 was computed. netstandard1.6 netstandard1.6 was computed. netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 is compatible. |
| .NET Framework | net451 net451 is compatible. net452 net452 was computed. net46 net46 was computed. net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen30 tizen30 was computed. tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Universal Windows Platform | uap uap was computed. uap10.0 uap10.0 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 5 NuGet packages that depend on Handlebars.Net:
| Package | Downloads |
|---|---|
|
Handlebars.Net.Helpers.Core
Core functionality for Handlebars.Net.Helpers |
|
|
EntityFrameworkCore.Scaffolding.Handlebars
Allows customization of classes reverse engineered from an existing database using the Entity Framework Core toolchain with Handlebars templates. |
|
|
Microsoft.SemanticKernel.PromptTemplates.Handlebars
Semantic Kernel Handlebars Prompt Template Engine |
|
|
Handlebars.Net.Extension.Json
System.Text.Json extension for Handlebars.Net |
|
|
Handlebars.Net.Extension.NewtonsoftJson
Newtonsoft.Json extension for Handlebars.Net |
Showing the top 20 popular GitHub repositories that depend on Handlebars.Net:
| Repository | Stars |
|---|---|
|
microsoft/semantic-kernel
Integrate cutting-edge LLM technology quickly and easily into your apps
|
|
|
bitwarden/server
Bitwarden infrastructure/backend (API, database, Docker, etc).
|
|
|
duplicati/duplicati
Store securely encrypted backups in the cloud!
|
|
|
dotnetcore/Util
Utilζ―δΈδΈͺ.NetεΉ³ε°δΈηεΊη¨ζ‘ζΆοΌζ¨ε¨ζεδΈε°ε’ιηεΌεθ½εοΌη±ε·₯ε
·η±»γεε±ζΆζεΊη±»γUiη»δ»ΆοΌι
ε₯代η ηζ樑ζΏοΌζιηη»ζγ
|
|
|
scriban/scriban
A fast, powerful, safe and lightweight scripting language and engine for .NET
|
|
|
ChangemakerStudios/Papercut-SMTP
Papercut SMTP -- The Simple Desktop Email Server
|
|
|
Squidex/squidex
Headless CMS and Content Managment Hub
|
|
|
exceptionless/Exceptionless
Exceptionless application
|
|
|
jwallet/spy-spotify
π€ Records Spotify to mp3 without ads and adds media tags to the files π΅
|
|
|
velopack/velopack
Installer and automatic update framework for cross-platform desktop applications
|
|
|
asynkron/protoactor-dotnet
Proto Actor - Ultra fast distributed actors for Go, C# and Java/Kotlin
|
|
|
featbit/featbit
Enterprise-grade feature flag platform that you can self-host. Get started - free.
|
|
|
sebastienros/fluid
Fluid is an open-source .NET template engine based on the Liquid template language.
|
|
|
statiqdev/Statiq
Statiq is a flexible static site generator written in .NET.
|
|
|
drasi-project/drasi-platform
The Data Change Processing platform
|
|
|
dotnet/dotnet
Home of .NET's Virtual Monolithic Repository which includes all the code needed to build the .NET SDK.
|
|
|
prom3theu5/aspirational-manifests
Handle deployments of .NET Aspire AppHost Projects
|
|
|
dotnet/arcade
Tools that provide common build infrastructure for multiple .NET Foundation projects.
|
|
|
jlucansky/Quartzmin
Quartzmin is powerful, easy to use web management tool for Quartz.NET
|
|
|
dotnetcore/SmartCode
SmartCode = IDataSource -> IBuildTask -> IOutput => Build Everything!!!
|
| Version | Downloads | Last Updated |
|---|---|---|
| 2.1.6 | 36,301,050 | 4/3/2024 |
| 2.1.5 | 166,382 | 4/1/2024 |
| 2.1.4 | 18,252,685 | 3/4/2023 |
| 2.1.3 | 728,876 | 2/15/2023 |
| 2.1.2 | 10,756,581 | 4/7/2022 |
| 2.1.1 | 659,920 | 3/26/2022 |
| 2.1.1-beta-1 | 11,345 | 3/4/2022 |
| 2.1.0 | 1,961,187 | 1/24/2022 |
| 2.0.10 | 973,828 | 12/22/2021 |
| 2.0.9 | 1,633,438 | 8/1/2021 |
| 2.0.8 | 723,593 | 6/2/2021 |
| 2.0.7 | 492,797 | 3/26/2021 |
| 2.0.6 | 46,702 | 3/21/2021 |
| 2.0.5 | 465,420 | 3/6/2021 |
| 2.0.4 | 4,868,858 | 1/7/2021 |
| 2.0.3 | 772,325 | 12/11/2020 |
| 2.0.2 | 35,835 | 12/8/2020 |
| 2.0.1 | 15,052 | 12/7/2020 |
| 2.0.0 | 33,298 | 12/6/2020 |
| 2.0.0-rc-2 | 2,774 | 12/2/2020 |