Skip to content

Templates

Constantin Mihai edited this page Jun 14, 2022 · 60 revisions

Templates are the basis of the eryn engine. There are multiple types of templates, all of which are listed below.

Normal Template

The simplest kind of template.

Syntax

[|block_of_code_here|]

Normal templates contain a block of JavaScript code. This block of code code is executed, and the result is then rendered. Currently, the supported result types are:

  • String
  • Number
  • Array
  • Object
  • Buffer since 0.2.0
  • null
  • undefined

Remarks

  • if the result is either of type String or Number, it is rendered as-is

  • if the result is either of type Array or Object, it is automatically stringified and rendered as a String

  • if the result is of type Buffer, the raw binary data is rendered since 0.2.0

  • if the result is either null or undefined, nothing will be rendered

  • if the template contains an object expression (i.e. {...}), eryn will automatically surround it with parentheses. This is because the evaluation function will treat it as a block if those parentheses are missing. This is only done if the first character of the template content is { since 0.2.8

  • if you want to declare a nested block at the beginning of the template content, you wouldn't want the first { character to be treated as an object expression. To treat it as a proper block, write dummy code before the opening bracket (e.g. /* BLOCK */ {...}). This way, the content won't start with { and eryn won't change your code. You only need to do this if the nested block is right at the beginning of the content (e.g. your template looks like [| {......} |]) since 0.2.8

  • the last two remarks apply to ALL template types since 0.2.8

Code execution

The execution of the blocks of code is done using the eval function through a bridge.

Because of this, normal templates offer great flexibility, as they can contain almost any JavaScript code. However, they can also be dangerous if not written properly. See Security Concerns for more info.

Example

Consider the following context:

{
    firstName: "Tyler",
    lastName: "Bounty",
    strings: ["hello", "world"]
}

This:

The context is [|context|]

Hello, [|context.firstName|] [|context.lastName|]!

The last 3 chars of the first string: [| context.strings[0].substring(context.strings[0].length - 3) |]

...will be rendered as:

The context is {"firstName":"Tyler","lastName":"Bounty","strings":["hello","world"]}

Hello, Tyler Bounty!

The last 3 chars of the first string: llo

Void Template since 0.2.0

Like a normal template, except it doesn't render anything.

Syntax

[|# block_of_code_here|]

Void templates are useful when you want to execute a block of code silently (e.g. when declaring a local variable).

Example

This:

[|# local.x = 5 |]

[| local.x |]

...will be rendered as:

5

However, this:

[| local.x = 5 |]

[| local.x |]

...will be rendered as:

5

5

The void template suppresses the unwanted output from the local variable assignment.

Conditional Template

A template that acts like an if statement.

Syntax

[|? expression|]
body
[|end|]

Just like normal templates, conditional templates also support JavaScript blocks of code. In this case, the block of code (expression) is executed, and the result is converted to a boolean value. The body will be only rendered if the result is true.

Remarks

  • the body can contain anything, including other templates

  • if the conditional template isn't closed with a body end ([|end|]), an exception will be thrown

  • if a template starts with end but also contains other non-whitespace characters (e.g. the template [|end stuff|]), an exception will be thrown. A template body end must only contain the end marker, and may also contain whitespace characters (i.e. \r, \n, \t and space)

Else Conditional Template since 0.2.6

Else conditional templates are like else if, while else templates are like else. They are both optional.

The conditional template can be extended with else (conditional) templates. An else conditional template will have its body rendered only if the expression is evaluated true, and all previous else conditional templates have expressions that were evaluated to false. The else template is similar, but it doesn't have an expression.

When using else templates, the body end ([|end|]) should be placed after the final else (conditional) template.

All else (conditional) templates are optional. However, if an else template is included, it must be the last and it must also be ended with the body end ([|end|]).

Syntax example

[|? expression|]
body
[|:? expression1|]
else if 1
[|:? expression2|]
else if 2
[|:|]
else
[|end|]

The templates marked with :? act like else if, while the template marked with : acts like else. Note how the body end ([|end|]) is written after the else template.

Example

Consider the following context:

{
    name: "Tyler",
    renderGreeting: true,
    showTitle: false,
    randomObject: {
        hello: "world",
        nums: [10, 20, 30, 40]
    }
}

This:

[|? context.renderGreeting|]
Hello, [|context.name|].
[|end|]
[|? context.showTitle|]
Title goes here.
[|end|]
[|? context.randomObject.nums.length >= 4|]
The length is >= 4.
[|end|]

...will be rendered as:

Hello, Tyler.

The length is >= 4.

Inverted Conditional Template since 0.2.0 until 0.3.0 (exclusive)

until 0.3.0 This template type was removed in version 0.3.0. The information is left here in case older versions are still used.

Like a conditional template, except that it negates the expression.

Syntax

[|! expression|]
body
[|end|]

The body will be only rendered if the result is false.

Remarks

  • the inverted conditional template can also be extended with else conditional and else templates. However, there is no else inverted conditional

  • the body can contain anything, including other templates

  • if the inverted conditional template isn't closed with a body end ([|end|]), an exception will be thrown

  • if a template starts with end but also contains other non-whitespace characters (e.g. the template [|end stuff|]), an exception will be thrown. A template body end must only contain the end marker, and may also contain whitespace characters (i.e. \r, \n, \t and space)

Loop Template

A template that acts like a forEach statement.

Syntax

[|@ iterator : array|]
body
[|end|]

since 0.2.0

[|@ iterator : array ~|]
body
[|end|]

The body will be rendered for each element of the array. During each iteration, the current element is stored in the variable with the name specified by the iterator.

If the loop contains a reverse marker (~) at the end, all elements/properties will be iterated over in reverse order. since 0.2.0

Remarks

  • the body can contain anything, including other templates

  • the array must be either of type Array or Object

  • if the array is an object, the loop will iterate through the properties. In this case, the iterator will be an object: { key: property_key, value: property_value } since 0.2.0

  • the iterator will be stored in the local object (see here) since 0.2.0

  • if the local object is modified inside the body, the changes will not always persist (see #23). A shallow copy of the object is made before entering the loop, and is restored at the end of the loop (and at the end of each iteration, in order to instantiate the iterator again). If you want to make sure that those changes will never persist, use the enableDeepCloning option. This way, any modifications done to the local object inside the loop body will have no effect outside of the body since 0.2.0

  • modifying the iterator inside the body is not guaranteed to also modify the original element. If you want to make sure that modifying the iterator won't have any effect on the original element, use the cloneIterators and enableDeepCloning options since 0.2.0

  • the context object will not be backed up, unlike the local object. Therefore, any modifications done to it will persist even after the loop body

  • just like conditional templates, loop templates need to be closed with a body end ([|end|]). Not closing the template will result in an exception being thrown

  • if a template starts with end but also contains other non-whitespace characters (e.g. the template [|end stuff|]), an exception will be thrown. A template body end must only contain the end marker, and may also contain whitespace characters (i.e. \r, \n, \t and space)

Example

Consider the following context:

{
    users: [
        {
            name: "Tyler",
            age: 21
        },
        {
            name: "Alex",
            age: 22
        },
        {
            name: "Tom",
            age: 24
        }
    ]
}

This:

[|@ user : context.users|]
My name is [|user.name|] and I am [|user.age|] years old.
[|end|]

will be rendered as:

My name is Tyler and I am 21 years old.

My name is Alex and I am 22 years old.

My name is Tom and I am 24 years old.

Component Template

Renders the contents of another file, with optional context and content.

Syntax

[|% comp_path|]
content
[|end|]
[|% comp_path /|]
[|% comp_path : context|]
content
[|end|]
[|% comp_path : context /|]

There are 2 types of component templates:

  • regular: has content, and must be closed with [|end|]

  • self-closed: doesn't have content

A component template contains:

  • path: the path to the component

  • context (optional): the context object with which to render the component (undefined by default)

  • content (optional): the content with which to render the component

The context is a block of JavaScript code. The code will be executed, and the result will be assigned to the context variable. Think of it like this:

var backup = context;
context = block_of_code;

// ... after the component was rendered
context = backup;

The content of a component can be accessed with a normal template, by using the content keyword.

[| content |]

Remarks

  • the content can contain anything, including templates

  • the content only makes sense when talking about regular components. Self-closing components, as well as files rendered using the render function, have no content

  • the context object inside the content refers to the parent context

  • the content keyword inside the content refers to the parent content, NOT the component content (as that would result in an infinite loop)

  • just like conditional and loop templates, regular component templates need to be closed with a body end ([|end|]). Not closing the template will result in an exception being thrown

  • evidently, self-closing component templates must not to be closed with a body end. Doing so may result in an exception being thrown

  • local objects will not be shared with any components (see here) since 0.2.0

  • if a template starts with / but also contains other non-whitespace characters (e.g. the template [|end stuff|]), it is an exception will be thrown. A body end must only contain the end marker, and may also contain whitespace characters (i.e. \r, \n, \t and space)

Example

Consider the following context:

{
    title: {
        text: "Hello",
        size: 16
    }
}

This:

Here are the components:
[|% comp.eryn : context.title|]
This is the content. It can see the context: [|context|]
[|end|]
[|% comp.eryn : {text: "World", size: 32}|]
This is the content. It can see the context: [|context|]
[|end|]
[|% comp2.eryn /|]

comp.eryn

The title is [|context.text|] and has a size of [|context.size|].
[|content|]

comp2.eryn

This is a component with no content or context.

...will be rendered as:

Here are the components:
The title is Hello and has a size of 16.

This is the content. It can see the context: {"text":"Hello","size":16}

The title is World and has a size of 32.

This is the content. It can see the context: {"text":"World","size":32}

This is a component with no content or context.

Comment Template since 0.2.0

A template that contains a comment, which is ignored by the compiler.

Syntax

[|// comment_here //|]

[|// multiline
comment //|]

Comment templates are ignored by the compiler, and don't appear in the compiled output. Therefore, they won't have any impact on the rendering process.

Unlike other template types, comment templates don't end with templateEnd (default: |]). Instead, they end with commentTemplateEnd (default: //|]). See here why.

Example

This:

Hello, [|// this is a comment //|]
World

[|// this is a multiline
comment
//|]

...will be rendered as:

Hello,
World

Intro

    Home

    Getting Started

Engine Basics

    Context

    Templates

    Local

    Shared

    Modes

Functions

    Compile

    Render

    Options

Other

    Security Concerns

    Known Issues

Clone this wiki locally