Fluent templates are an alternative way to declare CodeOnly templates that can be more concise than JSON-like structured declarations.
Templates declared this way are constructed using a fluent-style API.
$ Function
To use fluent templates, first import the $
function:
import { $ } from "@codeonlyjs/core"
The $
function is used to construct a fluent template node:
$("div")
yields:
{
type: "div"
}
For element names that conform to JavaScript identifiers, accessing
a property on the $
function also create elements:
$.div // equivalent to $("div")
Once you've constructed a fluent node, you can set its properties by calling functions on the object with the property's name.
Each function you call returns the original fluent node which allows chaining:
$.div.id("my-id").class("class")
yields:
{
type: "div",
id: "my-id",
class: "class"
}
While it might appear that fluent templates are limited to a set of hard-coded element and attribute names, this isn't the case.
The fluent node object uses JavaScript proxies to detect the property names as they're invoked allowing any element/attribute name to be set.
ie: $.foo.bar("baz")
will give <foo bar="baz">
- even though CodeOnly
doesn't include a property named foo
nor a function named bar
.
type Property
The type
property is automatically mapped to attr_type
resolving
the conflict between the HTML type
attribute and the CodeOnly type
property.
ie: this works as expected
$.input.type("password")
Dynamic callbacks can be used anywhere they can be used in structured templates:
$.span.text(c => `Count: ${c.count}`)
yields:
{
type: "span",
text: c => `Count: ${c.count}`,
}
If the class
and style
methods are called with more than one
parameter they are converted to boolean classes and named styles:
[
$.div.class("selected", c => c.isSelected),
$.div.style("color", c => c.color),
]
yields
[
{
type: "div",
class_selected: c => c.isSelected,
},
{
type: "div",
style_color: c => c.color,
}
]
Connecting event handlers works as expected:
{
$.input.type("text").on_click(c => c.onClick());
}
Fluent templates also allow this alternate format:
{
$.input.type("text").on("click", c => c.onClick());
}
A fluent node is a function that appends child nodes to the element being constructed.
The append function accepts a variable number of arguments and all arguments will be appended as child nodes.
In this example, the container has two child items, and each item has a text child node ("Apples" and "Pears")
$.div.class("container")(
$.div.class("item1")("Apples")
$.div.class("item2")("Pears")
)
The append function returns the same fluent node object so chaining order for content vs attributes doesn't matter:
// This...
$.div.class("item2")("Pears")
// is equivalent to this
$.div("Pears").class("item2")
You can also call the append function multiple times:
$.div
($.span("apples")) // Append first child span
($.span("pears")) // Append second child span
Appending an array will append each item:
$.div(makeChildren());
function makeChildren()
{
return [
$.span("apples"),
$.span("pears")
]
}
To reference a component using the fluent method, pass the
component constructor to the $()
function:
$(MyComponent).title("My Component")
yields:
{
type: MyComponent,
title: "My Component"
}
The fluent and structured formats can be inter-mixed:
eg: structured inside fluent
$.div.class("container")(
{ type: "div", class: "item1", $: "Apples" },
{ type: "div", class: "item1", $: "Pears" },
)
or fluent inside structured:
{
type: "div",
class: "container",
$: [
$.div.class("item1")("Apples"),
$.div.class("item2")("Pears")
]
}
A fluent node represents a template node that's under construction.
The actual structured template of a fluent node is available as the
$node
property.
Unwrapping is done automatically by the template compiler so usually you shouldn't need to do this unless you're trying to manually combine fluent and structured templates.
Both template formats can be used to express any kind of content so their use often comes down to personal preference.
However as general guide:
The structured format works best for block level content (panels, sections, headers, etc...)
The fluent format works best for inline-span level content (paragraphs, links, spans, images etc...)
Also, we don't recommend using the fluent method for nodes using
template directives such as foreach
and if
- these are more
clearly expressed using the structured format.