With Blocks

The {#with} tag shifts local scope to a sub-object for the duration of its block. Tags inside bind directly against that sub-object.

Basic syntax

{#with Path.To.Subobject}
…tags here bind against the subobject…
{/with}

Inside the block, $ refers to the with-target. Bare bind expressions resolve against it, and ..\ walks back to the enclosing scope (see Scope Chain Walk).

Example

Data:
var account = new
{
    Person = new
    {
        Name = new { First = "John", Last = "Doe" },
        Address = new
        {
            Line1 = "112 Main St.",
            Line2 = "Suite 210",
            City = "Plano",
            State = "TX",
            Zip = "75075"
        },
        Employer = "Hat Trick Labs"
    }
};
Template:
<div>Active Account:</div>
{#with Person.Name}
<div>{First} {Last}</div>
{/with}
<div>Address:</div>
{#with Person.Address}
<div>{Line1}{#if Line2}</br>{Line2}{/if}</div>
<div>{City}, {State} {Zip}</div>
{/with}
Result:
<div>Active Account:</div>
<div>John Doe</div>
<div>Address:</div>
<div>112 Main St.</br>Suite 210</div>
<div>Plano, TX 75075</div>

Without the scope shift, Person.Address. would repeat on each of the six tags in the block.

Why use {#with} over compound binds

Two practical wins:

  • Less repetition — when half a dozen tags all live under the same path, shifting once eliminates the noise of repeating the path on each tag.
  • Reusable sub-templates — a sub-template has no knowledge of the parent context; it only needs to know the shape of the {#with} target. An address fragment knows {Line1}, {City}, {State}, {Zip} and nothing else — wrap it in any {#with} pointing at an address-shaped object and it works regardless of what the parent template is bound to.

This composes well with Partials — a partial authored against a flat scope can be invoked from anywhere by wrapping the call in a {#with}.

Targeting falsy values

If the with-expression is falsy, the block is not rendered. See Core Concepts for the full falsy ruleset.

To render an “absent” message when the with-target is falsy, pair it with {#if !Path}:

{#with Person.Address}
<address>{Line1}, {City}</address>
{/with}
{#if !Person.Address}
<p>No address on file.</p>
{/if}

Reaching the outer scope

Inside a {#with} block, the parent scope is one level back. Reach it via ..\:

{#with Person.Address}
{City}, {State} — {..\Person.Name.First}'s address
{/with}

Or declare a variable in the outer scope first if the outer value is needed in multiple places. See Scope Chain Walk for the tradeoff.

Nesting

{#with} blocks can be nested freely and combined with {#if}, {#each}, and partials. Each {#with} contributes one level to the scope chain (and one frame to the stack-depth budget — see Stack Depth Limit).

Whitespace

Like other block tags, {#with} accepts - trim markers and is affected by engine-level TrimWhitespace. See Whitespace Control.

Next steps

  • Partials — compose reusable fragments that lean on {#with} for scope shaping
  • Scope Chain Walk — walk back out of a with-target
  • Variables — name an outer value once and reuse it from any nested block