Skip to content

Interpolation

{{ identifier }} (or {{{ identifier }}} for unescaped output) resolves identifier against the current render context and emits the result. This page covers every identifier form kBars recognizes and the order in which they're tried.

Identifier forms, in priority order

Form Resolves to
this / . The entire current top-of-stack context
this.foo, this/foo foo resolved against the current context (dot-notation / RFC 6901)
./foo Same as this/foo
../foo, ../../foo, … foo resolved against an ancestor context, one .. per level up
@env/name, @env.name An environment property — see Environment Properties
@local/name, @local.name A scoped variable bound with {{= }} — see Scoped Variables
@index, @key, or ../@index, ../@key The current (or an ancestor) loop position — see below
anything else Resolved against the current context, as dot-notation or an RFC 6901 pointer

The last row — "anything else" — is the common case and is dispatched by kPointer's KPointer.from(identifier):

  • Identifiers starting with / are RFC 6901 JSON Pointers: {{ /user/name }}.
  • Identifiers starting with # are RFC 6901 URI fragment identifiers (percent-encoding is decoded before resolution): {{ #/user/name }}.
  • Everything else is Mustache-style dot notation: {{ name }}, {{ user.city }}. A leading dot is stripped; trailing or consecutive dots throw kbParseException at parse time.

An identifier that resolves to nothing (an absent path, or a null context) renders as the literal text "null" — see Blocks for how the same absence is treated as falsy inside {{#if}}/{{#unless}}.

this / . and dotted/pointer suffixes

{{ this }}          {{! the whole current context }}
{{ . }}              {{! same as this }}
{{ this.name }}      {{! same as {{ name }} }}
{{ this/name }}      {{! same as {{ /name }} — RFC 6901 form }}
{{ ./name }}         {{! same as this/name }}

../ — walking up the context stack

Every {{#each}} iteration and {{#with}} block pushes a new context frame. ../ walks back up that stack, one level per repetition, before resolving the remainder:

{{#each posts}}
  {{ title }}
  {{#each comments}}
    by {{ author }}, on {{ ../title }}
  {{/each}}
{{/each}}

Inside the inner {{#each}}, ../title steps back out to the enclosing post's context before reading title. ../../title would step back two levels.

@index / @key — loop position

Inside {{#each}}, the current position is available as a special identifier read from the innermost active loop frame:

  • Iterating a list pushes @index — the zero-based position of the current element.
  • Iterating a struct (JSON object / map) pushes @key — the current entry's key, and iterates its entries in insertion order (not sorted, not hash order — this is a normative property of the conformance suite; see Iteration Order).
{{#each items}}{{ @index }}: {{ this }}
{{/each}}

{{#each user}}{{ @key }} = {{ this }}
{{/each}}

Prefix with ../ to read an enclosing loop's position from inside a nested loop — ../@index, ../@key — the same ancestor-walking rule as any other ../-prefixed identifier.

Literal values

An interpolation expression can also be a literal instead of an identifier:

  • Strings: "…" or '…', with escape sequences \\, \", \', \n, \r, \t, \uXXXX.
  • Numbers: -?[0-9]+(\.[0-9]+)? (no scientific notation in source; see Number Rendering for how numbers render).
  • Booleans and null: true, false, null.
{{ "hello" }}   {{! hello }}
{{ 123 }}       {{! 123 }}
{{ true }}      {{! true }}

String primitives render unquoted; other values render in their Kpa form. See Expressions for how literals combine with binary operators, and Transforms for piping any of these through |.