Blocks

Blocks lets you define reusable statements that can be invoked with a new context allowing the creation custom iterators and helpers - making it easy to encapsulate reusable functionality and reduce boilerplate for common functionality.

The syntax for blocks follows the familiar handlebars block helpers in both syntax and functionality. Templates also includes most of handlebars.js block helpers which are useful in a HTML template language whilst minimizing any porting efforts if needing to reuse existing JavaScript handlebars templates.

We'll walk through creating a few of the built-in Template blocks to demonstrate how to create them from scratch.

noop

We'll start with creating the noop block (short for "no operation") which functions like a block comment by removing its inner contents from the rendered page:

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
  {{#noop}}
    <h2>Removed Content</h2>
    {{page}}
  {{/noop}}
  </div>
</div>

The noop block is also the smallest implementation possible which needs to inherit TemplateBlock class, overrides the Name getter with the name of the block and implements the WriteAsync() method which for the noop block just returns an empty Task there by not writing anything to the Output Stream, resulting in its inner contents being ignored:

public class TemplateNoopBlock : TemplateBlock
{
    public override string Name => "noop";

    public override Task WriteAsync(TemplateScopeContext scope, PageBlockFragment block, CancellationToken ct)
        => Task.CompletedTask;
}

All Block's are executed with 3 parameters:

Registering Blocks

The same flexible registration options for Registering Filters is also available for registering blocks where if it wasn't already built-in, TemplateNoopBlock could be registered by adding it to the TemplateBlocks collection:

var context = new TemplateContext {
    TemplateBlocks = { new TemplateNoopBlock() },
}.Init();
Autowired using TemplateContext IOC

Autowired instances of blocks and filters can also be created using TemplateContext's configured IOC where they're also injected with any registered IOC dependencies by registering them in the ScanTypes collection:

var context = new TemplateContext
{
    ScanTypes = { typeof(TemplateNoopBlock) }
};
context.Container.AddSingleton<ICacheClient>(() => new MemoryCacheClient());
context.Init();

When the TemplateContext is initialized it will go through each Type and create an autowired instance of each Type and register them in the TemplateBlocks collection. An alternative to registering individual Types is to register an entire Assembly, e.g:

var context = new TemplateContext
{
    ScanAssemblies = { typeof(MyBlock).Assembly }
};

Where it automatically registers any Blocks or Filters contained in the Assembly where the MyBlock Type is defined.

bold

A step up from noop is the bold Template Block which markup its contents within the <b/> tag:

{{#bold}}This text will be bold{{/bold}}

Which calls the base.WriteBodyAsync() method to evaluate and write the Block's contents to the OutputStream using the current TemplateScopeContext:

public class TemplateBoldBlock : TemplateBlock
{
    public override string Name => "bold";
    
    public override async Task WriteAsync(
        TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        await scope.OutputStream.WriteAsync("<b>", token);
        await WriteBodyAsync(scope, block, token);
        await scope.OutputStream.WriteAsync("</b>", token);
    }
}

with

The with Block shows an example of utilizing arguments. To maximize flexibility arguments passed into your block are captured in a free-form string (specifically a ReadOnlyMemory<char>) which allows creating Blocks varying from simple arguments to complex LINQ-like expressions - a feature some built-in Blocks take advantage of.

The with block works similarly to handlebars with helper or JavaScript's with statement where it extracts the properties (or Keys) of an object and adds them to the current scope which avoids needing a prefix each property reference, e.g. being able to use {{Name}} instead of {{person.Name}}:

{{#with person}}
    Hi {{Name}}, your Age is {{Age}}.
{{/with}}

Also the with Block's contents are only evaluated if the argument expression is null.

The implementation below shows the optimal way to implement with by calling GetJsExpressionAndEvaluate() to resolve a cached AST token that's then evaluated to return the result of the Argument expression.

If the argument evaluates to an object it calls the ToObjectDictionary() extension method to convert it into a Dictionary<string,object> then creates a new scope with each property added as arguments and then evaluates the block's Body contents with the new scope:

public class TemplateWithBlock : TemplateBlock
{
    public override string Name => "with";

    public override async Task WriteAsync(
        TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var result = block.Argument.GetJsExpressionAndEvaluate(scope,
            ifNone: () => throw new NotSupportedException("'with' block does not have a valid expression"));

        if (result != null)
        {
            var resultAsMap = result.ToObjectDictionary();

            var withScope = scope.ScopeWithParams(resultAsMap);
             
            await WriteBodyAsync(withScope, block, token);
        }
    }
}

To better highlight how this works, a non-cached version of GetJsExpressionAndEvaluate() involves parsing the Argument string into an AST Token then evaluating it with the current scope:

block.Argument.ParseJsExpression(out token);
var result = token.Evaluate(scope);

The ParseJsExpression() extension method is able to parse virtually any JavaScript Expression into an AST tree which can then be evaluated by calling its token.Evaluate(scope) method.

Final implementation

The actual TemplateWithBlock.cs used in Templates includes extended functionality which uses GetJsExpressionAndEvaluateAsync() to be able to evaluate both sync and async results.

else if/else statements

It also evaluates any block.ElseBlocks statements which is functionality available to all blocks which are able to evaluate any alternative else/else if statements when the main template isn't rendered, e.g. in this case when the with block is called with a null argument:

public class TemplateWithBlock : TemplateBlock
{
    public override string Name => "with";

    public override async Task WriteAsync(
        TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var result = await block.Argument.GetJsExpressionAndEvaluateAsync(scope,
            ifNone: () => throw new NotSupportedException("'with' block does not have a valid expression"));

        if (result != null)
        {
            var resultAsMap = result.ToObjectDictionary();

            var withScope = scope.ScopeWithParams(resultAsMap);
            
            await WriteBodyAsync(withScope, block, token);
        }
        else
        {
            await WriteElseAsync(scope, block.ElseBlocks, token);
        }
    }
}

This enables the with block to also evaluate async responses like the async results returned in async Database filters, it's also able to evaluate custom else statements for rendering different results based on alternate conditions, e.g:

{{#with dbSingle("select * from Person where id = @id", { id }) }}
    Hi {{Name}}, your Age is {{Age}}.
{{else if id == 0}}
    id is required.
{{else}}
    No person with id {{id}} exists.
{{/with}}

if

Since all blocks are able to execute any number of {{else}} statements by calling base.WriteElseAsync(), the implementation for the {{if}} block ends up being even simpler which just needs to evaluate the argument to bool.

If true it writes the body with WriteBodyAsync() otherwise it evaluates any else statements with WriteElseAsync():

/// <summary>
/// Handlebars.js like if block
/// Usages: {{#if a > b}} max {{a}} {{/if}}
///         {{#if a > b}} max {{a}} {{else}} max {{b}} {{/if}}
///         {{#if a > b}} max {{a}} {{else if b > c}} max {{b}} {{else}} max {{c}} {{/if}}
/// </summary>
public class TemplateIfBlock : TemplateBlock
{
    public override string Name => "if";
    
    public override async Task WriteAsync(
        TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var result = await block.Argument.GetJsExpressionAndEvaluateToBoolAsync(scope,
            ifNone: () => throw new NotSupportedException("'if' block does not have a valid expression"));

        if (result)
        {
            await WriteBodyAsync(scope, block, token);
        }
        else
        {
            await WriteElseAsync(scope, block.ElseBlocks, token);
        }
    }
}

each

From what we've seen up till now, the handlebars.js each block is also straightforward to implement which just iterates over a collection argument that evaluates its body with a new scope containing the elements properties, a conventional it binding for the element and an index argument that can be used to determine the index of each element:

/// <summary>
/// Handlebars.js like each block
/// Usages: {{#each customers}} {{Name}} {{/each}}
///         {{#each customers}} {{it.Name}} {{/each}}
///         {{#each customers}} Customer {{index + 1}}: {{Name}} {{/each}}
///         {{#each numbers}} {{it}} {{else}} no numbers {{/each}}
///         {{#each numbers}} {{it}} {{else if letters != null}} has letters {{else}} no numbers {{/each}}
/// </summary>
public class TemplateSimpleEachBlock : TemplateBlock
{
    public override string Name => "each";

    public override async Task WriteAsync(
        TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var collection = (IEnumerable) block.Argument.GetJsExpressionAndEvaluate(scope,
            ifNone: () => throw new NotSupportedException("'each' block does not have a valid expression"));

        var index = 0;
        if (collection != null)
        {
            foreach (var element in collection)
            {
                var scopeArgs = element.ToObjectDictionary();
                scopeArgs["it"] = element;
                scopeArgs[nameof(index)] = index++;
                
                var itemScope = scope.ScopeWithParams(scopeArgs);
                await WriteBodyAsync(itemScope, block, token);
            }
        }
        
        if (index == 0)
        {
            await WriteElseAsync(scope, block.ElseBlocks, token);
        }
    }
}

Despite its terse implementation, the above Template Block can be used to iterate over any expression that evaluates to a collection, inc. objects, POCOs, strings as well as Value Type collections like ints.

Built-in each

However the built-in TemplateEachBlock.cs has a larger implementation to support its richer feature-set where it also includes support for async results, custom element bindings and LINQ-like syntax for maximum expressiveness whilst utilizing expression caching to ensure any complex argument expressions are only parsed once.

/// <summary>
/// Handlebars.js like each block
/// Usages: {{#each customers}} {{Name}} {{/each}}
///         {{#each customers}} {{it.Name}} {{/each}}
///         {{#each num in numbers}} {{num}} {{/each}}
///         {{#each num in [1,2,3]}} {{num}} {{/each}}
///         {{#each numbers}} {{it}} {{else}} no numbers {{/each}}
///         {{#each numbers}} {{it}} {{else if letters != null}} has letters {{else}} no numbers {{/each}}
///         {{#each n in numbers where n > 5}} {{it}} {{else}} no numbers > 5 {{/each}}
///         {{#each n in numbers where n > 5 orderby n skip 1 take 2}} {{it}} {{else}}no numbers > 5{{/each}}
/// </summary>
public class TemplateEachBlock : TemplateBlock { ... }

By using ParseJsExpression() to parse expressions after each "LINQ modifier", each supports evaluating complex JavaScript expressions in each of its LINQ querying features, e.g:

Custom bindings

When using a custom binding like {{#each c in customers}} above, the element is only accessible with the custom c binding which is more efficient when only needing to reference a subset of the element's properties as it avoids adding each of the elements properties in the items execution scope.

Check out LINQ Examples for more live previews showcasing advanced usages of the {{#each}} block.

raw

The {{#raw}} block is similar to handlebars.js's raw-helper which captures the template's raw text content instead of having its content evaluated, making it ideal for emitting content that could contain template expressions like client-side JavaScript or template expressions that shouldn't be evaluated on the server such as Vue, Angular or Ember templates:

{{#raw}}
<div id="app">
    {{ message }}
</div>
{{/raw}}

When called with no arguments it will render its unprocessed raw text contents. When called with a single argument, e.g. {{#raw varname}} it will instead save the raw text contents to the specified global PageResult variable and lastly when called with the appendTo modifier it will append its contents to the existing variable, or initialize it if it doesn't exist.

This is now the preferred approach used in all .NET Core and .NET Framework Web Templates for pages and partials to append any custom JavaScript script blocks they need on the page, e.g:

{{#raw appendTo scripts}}
<script>
    //...
</script>
{{/raw}}

Where any captured custom scripts are rendered at the bottom of _layout.html with:

    <script src="/assets/js/default.js"></script>

    {{ scripts | raw }}

</body>
</html>

The implementation to support each of these usages is contained within TemplateRawBlock.cs below which inspects the block.Argument to determine whether it should capture the contents into the specified variable or write its raw string contents directly to the OutputStream:

/// <summary>
/// Special block which captures the raw body as a string fragment
///
/// Usages: {{#raw}}emit {{ verbatim }} body{{/raw}}
///         {{#raw varname}}assigned to varname{{/raw}}
///         {{#raw appendTo varname}}appended to varname{{/raw}}
/// </summary>
public class TemplateRawBlock : TemplateBlock
{
    public override string Name => "raw";
    
    public override async Task WriteAsync(
        TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var strFragment = (PageStringFragment)block.Body[0];

        if (!block.Argument.IsNullOrWhiteSpace())
        {
            Capture(scope, block, strFragment);
        }
        else
        {
            await scope.OutputStream.WriteAsync(strFragment.Value.Span, token);
        }
    }

    private static void Capture(
        TemplateScopeContext scope, PageBlockFragment block, PageStringFragment strFragment)
    {
        var literal = block.Argument.Span.AdvancePastWhitespace();
        bool appendTo = false;
        if (literal.StartsWith("appendTo "))
        {
            appendTo = true;
            literal = literal.Advance("appendTo ".Length);
        }

        literal = literal.ParseVarName(out var name);
        var nameString = name.Value();
        if (appendTo && scope.PageResult.Args.TryGetValue(nameString, out var oVar)
                        && oVar is string existingString)
        {
            scope.PageResult.Args[nameString] = existingString + strFragment.Value;
            return;
        }

        scope.PageResult.Args[nameString] = strFragment.Value.ToString();
    }
}

capture

The {{#capture}} block is similar to the raw block except instead of using its raw text contents, it instead evaluates its contents and captures the output. It also supports evaluating the contents with scoped arguments where by each property in the object dictionary is added in the scoped arguments that the template is executed with:

/// <summary>
/// Captures the output and assigns it to the specified variable.
/// Accepts an optional Object Dictionary as scope arguments when evaluating body.
/// Effectively is similar 
///
/// Usages: {{#capture output}} {{#each args}} - [{{it}}](/path?arg={{it}}) {{/each}} {{/capture}}
///         {{#capture output {nums:[1,2,3]} }} {{#each nums}} {{it}} {{/each}} {{/capture}}
///         {{#capture appendTo output {nums:[1,2,3]} }} {{#each nums}} {{it}} {{/each}} {{/capture}}
/// </summary>
public class TemplateCaptureBlock : TemplateBlock
{
    public override string Name => "capture";

    public override async Task WriteAsync(
        TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var (name, scopeArgs, appendTo) = Parse(scope, block);

        using (var ms = MemoryStreamFactory.GetStream())
        {
            var useScope = scope.ScopeWith(scopeArgs, ms);

            await WriteBodyAsync(useScope, block, token);

            var capturedOutput = ms.ReadToEnd();

            if (appendTo && scope.PageResult.Args.TryGetValue(name, out var oVar)
                         && oVar is string existingString)
            {
                scope.PageResult.Args[name] = existingString + capturedOutput;
                return;
            }
        
            scope.PageResult.Args[name] = capturedOutput;
        }
    }

    //Extract usages of Span outside of async method 
    private (string name, Dictionary<string, object> scopeArgs, bool appendTo) 
        Parse(TemplateScopeContext scope, PageBlockFragment block)
    {
        if (block.Argument.IsNullOrWhiteSpace())
            throw new NotSupportedException("'capture' block is missing variable name to assign output to");
        
        var literal = block.Argument.AdvancePastWhitespace();
        bool appendTo = false;
        if (literal.StartsWith("appendTo "))
        {
            appendTo = true;
            literal = literal.Advance("appendTo ".Length);
        }
            
        literal = literal.ParseVarName(out var name);
        if (name.IsNullOrEmpty())
            throw new NotSupportedException("'capture' block is missing variable name to assign output to");

        literal = literal.AdvancePastWhitespace();

        var argValue = literal.GetJsExpressionAndEvaluate(scope);

        var scopeArgs = argValue as Dictionary<string, object>;

        if (argValue != null && scopeArgs == null)
            throw new NotSupportedException("Any 'capture' argument must be an Object Dictionary");

        return (name.ToString(), scopeArgs, appendTo);
    }
}

With this we can dynamically generate some markdown, capture its contents and convert the resulting markdown to html using the markdown Filter transformer:

{{#capture todoMarkdown { items:[1,2,3] } }}
## TODO List
{{#each items}}
  - Item {{it}}
{{/each}}
{{/capture}}

{{todoMarkdown | markdown}}

markdown

The {{#markdown}} block makes it even easier to embed markdown content directly in web pages which works as you'd expect where content in a markdown block is converted into HTML, e.g:

{{#markdown}}
## TODO List
  - Item 1
  - Item 2
  - Item 3
{{/markdown}}

Which is now the easiest and preferred way to embed Markdown content in content-rich hybrid web pages like Razor Rockstars content pages, or even this blocks.html WebPage itself which makes extensive use of markdown.

As markdown block only supports 2 usages its implementation is much simpler than the capture block above:

/// <summary>
/// Converts markdown contents to HTML using the configured MarkdownConfig.Transformer.
/// If a variable name is specified the HTML output is captured and saved instead. 
///
/// Usages: {{#markdown}} ## The Heading {{/markdown}}
///         {{#markdown content}} ## The Heading {{/markdown}} HTML: {{content}}
/// </summary>
public class TemplateMarkdownBlock : TemplateBlock
{
    public override string Name => "markdown";
    
    public override async Task WriteAsync(
        TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
    {
        var strFragment = (PageStringFragment)block.Body[0];

        if (!block.Argument.IsNullOrWhiteSpace())
        {
            Capture(scope, block, strFragment);
        }
        else
        {
            await scope.OutputStream.WriteAsync(MarkdownConfig.Transform(strFragment.ValueString), token);
        }
    }

    private static void Capture(
        TemplateScopeContext scope, PageBlockFragment block, PageStringFragment strFragment)
    {
        var literal = block.Argument.AdvancePastWhitespace();

        literal = literal.ParseVarName(out var name);
        var nameString = name.ToString();
        scope.PageResult.Args[nameString] = MarkdownConfig.Transform(strFragment.ValueString).ToRawString();
    }
}

Use Alternative Markdown Implementation

By default ServiceStack uses an interned implementation of MarkdownDeep for rendering markdown, you can get ServiceStack to use an alternate Markdown implementation by overriding MarkdownConfig.Transformer.

E.g. to use the richer Markdig implementation, install the Markdig NuGet package:

PM> Install-Package Markdig

Then assign a custom IMarkdownTransformer:

public class MarkdigTransformer : IMarkdownTransformer
{
    private Markdig.MarkdownPipeline Pipeline { get; } = 
        Markdig.MarkdownExtensions.UseAdvancedExtensions(new Markdig.MarkdownPipelineBuilder()).Build();

    public string Transform(string markdown) => Markdig.Markdown.ToHtml(markdown, Pipeline);
}

MarkdownConfig.Transformer = new MarkdigTransformer();

partial

The {{#partial}} block lets you create In Memory partials which is useful when working with partial filters like selectPartial as it lets you declare multiple partials within the same page, instead of requiring multiple individual files. See docs on Inline partials for a Live comparison of using in memory partials.

html

The purpose of the html blocks is to pack a suite of generically useful functionality commonly used when generating html. All html blocks inherit the same functionality with blocks registered for the most popular HTML elements, currently:

ul, ol, li, div, p, form, input, select, option, textarea, button, table, tr, td, thead, tbody, tfoot, dl, dt, dd, span, a, img, em, b, i, strong.

Ultimately they reduce boilerplate, e.g. you can generate a menu list with a single block:

{{#ul {each:items, id:'menu', class:'nav'} }} 
    <li>{{it}}</li> 
{{/ul}}

A more advanced example showcasing many of its different features is contained in the example below:

{{#ul {if:hasAccess, each:items, where:'Age > 27', 
        class:['nav', !disclaimerAccepted ? 'blur' : ''], id:`menu-${id}`, selected:true} }}
    {{#li {class: {alt:isOdd(index), active:Name==highlight} }} {{Name}} {{/li}}
{{else}}
    <div>no items</div>
{{/ul}}

This example utilizes many of the features in html blocks, namely:

All other properties like id and selected are treated like HTML attributes where if the property is a boolean like selected it's only displayed if its true otherwise all other html attribute's names and values are emitted as normal.

For a better illustration we can implement the same functionality above without using any html blocks:

{{#if hasAccess}}
    {{ items | where => it.Age > 27 | assignTo: items }}
    {{#if !isEmpty(items)}}
        <ul {{ ['nav', !disclaimerAccepted ? 'blur' : ''] | htmlClass }} id="menu-{{id}}">
        {{#each items}}
            <li {{ {alt:isOdd(index), active:Name==highlight} | htmlClass }}>{{Name}}</li>
        {{/each}}
        </ul>
    {{else}}
        <div>no items</div>
    {{/if}}
{{/if}}

The same functionality using C# Razor with the latest C# language features enabled can be implemented with:

@{
    var persons = (items as IEnumerable<Person>)?.Where(x => x.Age > 27);
}
@if (hasAccess)
{
    if (persons?.Any() == true)
    {
        <ul id="menu-@id" class="nav @(!disclaimerAccepted ? "hide" : "")">
            @{
                var index = 0;
            }
            @foreach (var person in persons)
            {
                <li class="@(index++ % 2 == 1 ? "alt " : "" )@(person.Name == activeName ? "active" : "")">
                    @person.Name
                </li>
            }
        </ul>
    }
    else
    {
        <div>no items</div>
    }
}

Removing Blocks

Like everything else in Templates, all built-in Blocks can be removed. To make it easy to remove groups of related blocks you can just remove the plugin that registered them using the RemovePlugins() API, e.g:

var context = new TemplateContext()
    .RemovePlugins(x => x is TemplateDefaultBlocks) // Remove default blocks
    .RemovePlugins(x => x is TemplateHtmlBlocks)    // Remove all html blocks
    .Init();

Or you can use the OnAfterPlugins callback to remove any individual blocks or filters that were added by any plugin.

E.g. the capture block can be removed with:

var context = new TemplateContext {
        OnAfterPlugins = ctx => ctx.RemoveBlocks(x => x.Name == "capture")
    }
    .Init();

made with by ServiceStack