Templating
Syntax
Antlers uses two different sets of start and stop characters:
- 🦌 Deerheads:
<{and}>- Lets HTML through - 🖇 Brackets:
{and}- Escapes HTML and can only render variables
Unlike other templating languages which use syntax to distinguish between control flow and output, there is no difference in Antlers. In Antlers all constructs render output, even if that output is an empty string ('').
Variables
Access an instance variable with:
def render
<html>{@user}</html>
end
Variables can evaluate:
- An instance variable:
{@instance_var}or<{ @instance_var }> - A method call/local variable:
{method_or_var}or<{ method_or_var }> - A method chain:
{method_or_var.method}or<{ method_or_var.method }> - A string:
{"String"}or<{ "String" }>
Warning
Quote Brackets in HTML attributes to avoid them being misinterpreted as Deerheads:
❌ Bad: <html class={var}> - "Oh no I see }>!"
✅ Good: <html class="{var}"> - "Phew it's just }"
Components
Render a node named UserNode with:
def render
<html><{ UserNode }></html>
end
Important
The class referenced via <{ MyClass }> must implement a render instance method.
Props
def render
<html><{ UserNode user=@user }></html>
end
The UserNode class definition would accept these props like:
class UserNode < LowNode
def render(event:, user:)
<h1>{user.name}</h1>
end
end
Slots
def render
<html>
<{ Layout: }>
<{ UserNode user=@user }>
<{ :Layout }>
</html>
end
The Layout would look like:
class Layout < LowNode
def render(event:)
<header>...</header>
<{ :slot }>
<footer>...</footer>
end
end
Conditionals
# Block.
<{ if: @user.happy? }>
<{ UserNode user=@user }>
<{ :if }>
# Directive. [UNRELEASED]
<{ UserNode user=@user if: @user.happy? }>
Loops
Array
<{ for: user in: @users }>
<{ UserNode user=user }>
<{ :for }>
Hash
For a hash use for: key, value syntax.
<{ for: id, user in: @users }>
<{ UserNode id=id user=user }>
<{ :for }>
Forms
Forms can be created in a compositional way, mixing both Antlers syntax with regular form elements:
<{ form: '/submit' }>
<input type="submit" value="Submit">
<{ :form }>
Antlers generates additional markup behind the scenes:
- Sets the form's
actionto/submit - Sets the form's
methodtoPOST - Adds an anti-forgery token to prevent CSRF [UNRELEASED]
Change the POST method to GET with:
<{ form: '/search' method: 'GET' }>
<input type="search">
<input type="submit" value="search">
<{ :form }>
Forms can be created in a compositional way, mixing both Antlers syntax with regular form elements:
<{ form: '/submit' }>
<input type="submit" value="Submit">
<{ :form }>
Antlers generates additional markup behind the scenes:
- Sets the form's
actionto/submit - Sets the form's
methodtoPOST - Adds an anti-forgery token to prevent CSRF [UNRELEASED]
Change the POST method to GET with:
<{ form: '/search' method: 'GET' }>
<input type="search">
<input type="submit" value="search">
<{ :form }>
Label [UNRELEASED]
<{ label: 'Label' }>
Search [UNRELEASED]
<{ search: :query }>
Submit [UNRELEASED]
<{ submit: 'Search' }>
Parallelism [UNRELEASED]
Add parallelism where it makes sense and you can measure the performance outcome and keep data integrity.
Per sibling:
def render
# Both child nodes executed at the same time.
<{ parallelize: }>
<{ UserNode user=@user }>
<{ PostsNode posts=@posts }>
<{ :parallelize }>
end
Per block:
def render
# Each UserNode rendered at the same time.
<{ map: user in: @users :parallelize }>
<{ UserNode user=user }>
<{ :map }>
end
Per directive:
<{ UserNode user=user for: user in: @users :parallelize }>
Translations
Variables ({}) are also useful for embedding text in RBX without any syntax highlighting issues:
def render
<html>{"I'm just a string"}</html>
end
Text entered this way can be translated based on region, language or any arbitrary condition. [UNRELEASED]
Full Examples
Slot
class UserNode < LowNode
def initialize
@user = User.new(username: "Random User", bio: "I'm a person!")
end
def render
<html>
<{ Layout: title=@user.username }>
{@user.bio}
<{ :Layout }>
</html>
end
end
The Layout would look like:
class Layout < LowNode
def render(event:, title:)
<header>...</header>
<h1>{title}</h1>
<{ :slot }>
<footer>...</footer>
end
end
The result would be:
<header>...</header>
<h1>Random User</h1>
<p>I'm a person!</p>
<footer>...</footer>
Architecture
Antlers creates an Abstract Syntax Tree composed of the following AntlerNodes:
Leaf nodes:
PropNodeVarNode
Branch nodes:
RootNodeSlotNodeYieldNode- RendersAntlerNodes inside aSlotNode
sequenceDiagram
autonumber
participant LowLoad
participant LowNode
participant Template
LowLoad->>LowNode: Load node
LowNode->>Template: Load template
Template->>Template: Parse Antlers
Template->>LowNode: Store template
Note over LowLoad,Template: Render event
LowNode->>Template: Render node
Template-->>LowNode: Render child nodes