Variable Reference
HTML Template Engine supports a wide range of variables provided by your data context. You can insert these variables into your HTML using expressions like {{variable}}
. Below are some common usage patterns:
Plain Variables
Simply reference a variable by its name to output its value. For example, if you write:
<p>{{variable}}</p>
the template will insert the value of variable
where it appears.
Nested Variables
If your data contains nested objects, access sub-properties using dot notation. For instance, if your context includes an object called address
with properties such as street
and city
, you can use:
<p>
Street: {{address.street}}
</p>
<p>
City: {{address.city}}
</p>
This instructs the engine to look up the address object first, then retrieve the specified field.
Arrays and Iteration
When a variable contains an array, you can iterate over its elements using the each
helper. For example, if you have an array named array_variable
, you might write:
<p>
<ul>
{{#each arrayVariable}}
<li>
{{@index}}: {{this}}
</li>
{{/each}}
</ul>
</p>
In this snippet, {{@index}}
provides the current iteration's index (starting at 0), and {{this}}
refers to the current array element. If the array elements are objects, you can access their properties directly:
<p>
<ul>
{{#each arrayVariable}}
<li>
{{id}} - {{description}}
</li>
{{/each}}
</ul>
</p>
Parent Context Access
When using block helpers like each
or with
, the current context changes. To reference a variable from an outer (parent) context, use the ../
notation. For example:
<p>
<p>Outer: {{variable}}</p>
</p>
<p>
<p>
{{#each items}}
<p>Inner: {{this}}</p>
<p>Parent variable: {{../variable}}</p>
{{/each}}
</p>
</p>
Here, {{../variable}}
accesses the variable from the parent context, even though the block's context is set to each element in items
.
Using these patterns, you can effectively insert and manipulate data within your templates—whether dealing with plain values, nested objects, or arrays—allowing you to dynamically display content based on your data context.
Built-in Helpers
HTML Template Engine supports these built-in helpers. Below is an overview of each relevant helper and examples of how to use them in your template:
if
Helper (Conditional Sections)
if
Helper (Conditional Sections)The if
helper conditionally renders a block of HTML based on whether a given expression is truthy or falsy. If the expression is truthy, the content inside the if
block is displayed; if it’s falsy
, the block is skipped. Template Engine considers the following as falsy values: false
, null
, undefined
, the empty string ""
, the number 0
, and an empty array []
. All other values are treated as truthy (including non-empty strings, non-zero numbers, objects, etc.).You can also specify an optional {{else}}
section for the case when the condition is falsy
. For example:
{{#if borrower_first_name}}
<p>Hello, {{borrower_first_name}} {{borrower_last_name}}!</p>
{{else}}
<p>Hello, valued customer!</p>
{{/if}}
In this example, if borrower_first_name
has a value (truthy), it will render a personalized greeting with the borrower’s name. If borrower_first_name
is not provided or empty (falsy), it will fall back to the "Hello, valued customer!"
message. Using if
in this way allows your template to handle optional data gracefully.
unless
Helper (Inverse Conditional)
unless
Helper (Inverse Conditional)The unless
helper is the inverse of if
. It will render the block inside it only if the given expression is falsy. In other words, {{#unless condition}} ... {{/unless}}
is equivalent to saying “if NOT condition, show this block.” For example, if we want to show a warning when a borrower’s last name is not provided, we could use unless
:
{{#unless borrower_last_name}}
<p class="warning">Warning: No last name provided for the borrower.</p>
{{/unless}}
This will output the warning paragraph only if borrower_last_name
is missing or falsy. If borrower_last_name
has a value, the block is skipped (and nothing is output, since we did not provide an else
for this case). The unless
helper is handy for handling the “false”
or empty scenario in your data without needing to explicitly negate an if
. (If you do need an else
with unless
, consider using if/else
for clarity.)
each
Helper (Looping over Lists or Objects)
each
Helper (Looping over Lists or Objects)Use the each
helper to iterate over an array or object and repeat a block for every element. When iterating over an array, the context inside the block becomes each element of the array in turn. When iterating over an object’s properties, the context becomes each value, and you can also access the property name (explained below). Inside an each
block, you can reference the current item with {{this}}
or by its properties. For example, if borrower_table
is an array of objects, you might do:
{{#each borrower_table}}
<p>Item {{@index}}: {{name}} (ID: {{id}})</p>
{{/each}}
- In this snippet,
{{#each borrower_table}}
will loop over each object in theborrower_table
array. Inside the loop:{{name}}
and{{id}}
refer to properties of the current item (assuming each object has those fields). You can use any field that the array element contains. {{@index}}
is a special data variable that gives the index of the current iteration (starting from0
for the first item). In the example, if an item is the first in the list,@index
is0
, so it will display"Item 0: ..."
. (See the Data Variables section for more about@index
and similar variables.)
If the array is empty or not present, by default nothing will be rendered inside the each
. You can provide an {{else}}
section to handle the "empty list" case:
{{#each borrower_table}}
<p>{{name}} – {{id}}</p>
{{else}}
<p>No items found.</p>
{{/each}}
Here, if borrower_table
has no elements, the message "No items found."
will be displayed instead of the list. This ensures your template doesn’t just output a blank area when there’s no data.Iterating over objects: If you use #each
on an object (for example, an address object), it will iterate over that object's own enumerable properties. In this case, you can use the @key
data variable inside the loop to get the property name:
{{#each borrower_home_address}}
<p>{{@key}}: {{this}}</p>
{{/each}}
If borrower_home_address
is { "street_name": "Main St", "city": "Metropolis" }
, this loop would output:
street_name: Main St
city: Metropolis
Each iteration sets {{this}}
to the property’s value (e.g., "Main St"
), and {{@key}}
to the property's name (e.g., "street_name"
). This is useful for dynamically listing out object fields.
with
Helper (Changing Context)
with
Helper (Changing Context)The with
helper allows you to dive into a nested object and treat it as the current context within a block. This means inside the with
block, you can reference the object's properties directly without repeating the parent object path. When the block ends, the context returns to the previous scope. For example, consider the borrower_home_address
object. Without with
, you might write:
<p>Address: {{borrower_home_address.street_name}}, {{borrower_home_address.city}}</p>
Using #with
, you can simplify this:
{{#with borrower_home_address}}
<p>Address: {{street_name}}, {{city}}</p>
{{/with}}
When borrower_home_address
exists, the content inside the block will output the street name and city directly. The with
block changes the context to borrower_home_address
, so {{street_name}}
is understood as borrower_home_address.street_name
. This makes the template cleaner, especially if you need to use multiple fields of that object. You can also provide an {{else}}
section in a with
block to handle the case where the value is missing or empty. For instance:
{{#with borrower_home_address}}
<p>Address: {{street_name}}, {{city}}</p>
{{else}}
<p>Address not available.</p>
{{/with}}
In this example, if borrower_home_address
is present, the address is shown. If it’s not provided (null
or undefined
), the template will output "Address not available."
instead. This pattern helps in showing fallback content when nested data is missing. Note: The with
helper only changes the context within its block; once the {{/with}}
is reached, the context goes back to what it was before. Also, if the value passed to with is falsy (e.g., an empty object or null
), the with
block is skipped and the else
(if present) is rendered.
Working with Context
Understanding how HTML Template Engine handles context is important for building templates. Context refers to the current scope of data that template engine is looking at while rendering. Here are some key points and examples on context in the template engine:
Top-level context
When the template begins rendering, the top-level context is the entire data object provided to the template (e.g., an object containing borrower_first_name
, borrower_last_name
, loan_amount
, etc.). At this level, you can access any top-level variable directly. For instance, in the top context, {{borrower_first_name}}
will output the first name as shown in the data.
Context inside helpers
Certain block helpers like each and with change the context for their inner block. When you enter an {{#each}}
block, the context shifts to the current item of the iteration. Similarly, inside a {{#with someObject}}
block, the context becomes someObject
. Other helpers like if
and unless
do not change the context – they simply conditionally render content, but the context inside an if
block remains the same as outside.
Example – context in each
:
<p>Borrower: {{borrower_first_name}}</p>
{{#each borrower_table}}
<p>{{borrower_first_name}} – Item ID: {{id}}</p>
{{/each}}
In this example, outside the each
, borrower_first_name
is directly available (top-level context). Inside the each
block, the context is now each element of borrower_table
. If each element has an id
field, {{id}}
outputs that. However, borrower_first_name
is not a property of the item in borrower_table
. To access the outer context (the borrower’s first name) from inside the loop, you need to reference the parent context. This is done by prefixing the variable with ../
. For instance, use {{../borrower_first_name}}
inside the each
to go “up” one level and get the value from the parent context
Revised example with parent context reference:
{{#each borrower_table}}
<p>{{../borrower_first_name}} – Item ID: {{id}}</p>
{{/each}}
Here, {{../borrower_first_name}}
will correctly display the borrower’s first name for each item because ../
tells template engine to look one level up the context stack for borrower_first_name
. The id
comes from the current item context. In effect, each rendered <p>
might look like “Alice – Item ID: 123”
, where "Alice"
is the outer context’s first name and 123
is the current item’s id.
The this
keyword
this
keywordIn any context, {{this}}
refers to the entire current context object/value. This is often used inside each when iterating over an array of simple values. For example, if borrower_table
was an array of names (just strings), inside {{#each borrower_table}}
, {{this}}
would represent the current name string. If the array element is an object, {{this}}
would refer to that object (which generally isn’t useful to print directly, but you could pass it to a helper or inspect it). In practice, you’ll typically use the property names (as shown above with {{id}}
or {{name}}
), but it’s good to know this represents the current context when needed.
Parent and root contexts
We saw above that ../
lets you move up to the parent context. Each ../
moves up one level. So if you are nested multiple levels (for example, inside an each
within an each
), you can use multiple ../../
segments to climb further up. Template Engine also provides a special data variable @root
which always points to the root context (the very top level data) no matter how deep you are. We will cover @root
in the next section, but keep in mind it’s another way to access global context.
Context and conditional helpers
As mentioned, #if
and #unless
do not create a new context. They simply check a value in the current context. This means inside an if
block, you can still access both the current context and any outer contexts normally without using ../
(since you haven’t moved into a new object/array context, you are still in the same scope, just conditionally rendering). The rule of thumb is: use ../
only when you are inside a block that changed the context (like each/with
). If you’re just inside an if
or unless
, your context hasn’t changed, so {{borrower_first_name}}
would still refer to the same value as it did outside the if
. (Attempting to use ../
in an if
when not needed could actually jump out to a higher context which might not be what you want.)
By keeping track of the context, you can correctly reference the data you need at each point in your template. When in doubt, remember:
- Same context: just use the variable name.
- Diving into an object/array: use
each
orwith
(context changes to that data). - Coming back out: use
../
(or@root
for global) to reference something from an outer scope.
Data Variables
HTML Template Engine provides special data variables (prefixed with @
) that give you additional information about the current context and iteration. These data variables are read-only and are set by HTML Template Engine automatically, especially during loops. You can use them in your template to make more dynamic decisions or displays. The available data variables in this template are:
@root
@root
This variable represents the root context, i.e. the top-level data object with which the template was executed. No matter how deep you are inside nested helpers, @root
lets you access the global context. For example, {{@root.borrower_first_name}}
will always get the first name from the top level of your data, even if you are inside an each
or with
block. This is useful if you are deeply nested and want to avoid using multiple ../
segments. (In our earlier example, {{../borrower_first_name}}
inside one loop could be replaced with {{@root.borrower_first_name}}
to directly fetch the top-level value.)
@index
@index
The zero-based index of the current item in an each
loop. This is only relevant inside an {{#each ...}}
block iterating over an array. The first iteration has @index = 0
, the second has @index = 1
, and so on. You can use @index
to display numbering or for calculations. For example, you might show a numbered list as in {{@index}}
. {{name}}
which would render "0. Alice"
, "1. Bob"
, etc., for an array of names. Keep in mind that @index
resets for each separate each
loop (it’s local to that loop’s context).
@first
@first
A boolean that is true
on the first iteration of an each
loop, and false
for all other iterations. Template engine sets @first = true
only for the first item in an array. You can use this to conditionally display something special for the first item. For example, you might add a different CSS class or include a header only before the first item.
{{#each items}}
{{#if @first}}<div class="first-item">First item:</div>{{/if}}
<p>{{this}}</p>
{{/each}}
In this snippet below, the “First item:”
label will appear once, before the first item’s <p>
, because @first
is true only at that point.
@last
@last
A boolean that is true
on the last iteration of an each
loop. It becomes true
when the loop is on its final item. This is useful for tasks like avoiding a trailing comma in a list or adding closing HTML after the last item. For instance:
{{#each tags}}
<span>{{this}}{{#unless @last}}, {{/unless}}</span>
{{/each}}
Here each
tag is followed by a comma unless it’s the last tag (we used unless @last
to skip the comma for the final item). This way, the list of tags will be comma-separated without an extra comma at the end.
@key
@key
The key name of the current property when iterating over an object with each. If your each
is looping over an object (as opposed to an array), template engine provides @key
to tell you the name of the current property.
{{#each borrower_home_address}}
<p>{{@key}}: {{this}}</p>
{{/each}}
As shown earlier, @key
would be "street_name"
for the first iteration (with this being "Main St"
), then "city"
for the next (with this being "Metropolis"
), etc. This lets you know which field you’re dealing with when you only have the value (this
) otherwise. (When iterating arrays, @key
is not typically used – @index
serves a similar role for array positions. In object iteration, there is no numeric index, so @key
is how you identify the property.)
These @
data variables are available only in relevant contexts (for example, you won’t have @index
outside of an each
loop, and @first/@last
make sense only in loops). They provide a powerful way to make your templates aware of their position in the data. Using data variables, you can write more dynamic templates. For instance, you can highlight the first item, number each entry, or reference the original data context at any depth. Keep in mind that @root
is particularly helpful if you pass the template a complex data object; it ensures you can always access any top-level property without worrying about how deep you are in block helpers.