@optionfactory/ftl
v0.26.0
Published
FTL is a templating library targeting browsers similar to thymeleaf
Downloads
11
Readme
FTL - HTML template library
FTL is a templating library targeting browsers similar to thymeleaf
Getting started
- Import the lib via CDN:
<script src="https://cdn.jsdelivr.net/npm/optionfactory/ftl@{VERSION}/dist/ftl.iife.min.js" integrity="{INTEGRITY}" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
- Init
EvaluationContext
, optionally registering custom modules
const ec = ftl.EvaluationContext.configure({
math : {
isEven: v => v % 2 === 0
}
});
- Define a template using a template tag, a string, a
DocumentFragment
or aNode
<body>
...
<div id="target"></div>
<template id="my-template">
<h1>{{title}}</h1>
</template>
...
</body>
- Create a template
const template = ftl.Template.fromSelector('#my-template', ec);
- Render the template
const data = {title: 'Hello World!'};
template.renderToSelector('#target', data);
Attributes evaluation
All attributes starting with data-tpl-
(case sensitive) are evaluated in the followind order:
data-tpl-if
data-tpl-with
data-tpl-each
data-tpl-value
data-tpl-class-append
data-tpl-attr-append
data-tpl-text
data-tpl-html
data-tpl-remove
data-tpl-*
data-tpl-if
Removes from the DOM the element if the expression evaluates as false
<h3>Tracking:</h3>
<p data-tpl-if="delivered">Your package has been delivered</p>
E.g:
data = {delivered: true}
<h3>Tracking:</h3>
<p>Your package has been delivered</p>
E.g:
data = {delivered: false}
<h3>Tracking:</h3>
data-tpl-with
Sets the context of the fragment to the specified value
data = {
parent: {
text: "I'm the parent obj",
nested: {
text: "I'm the nested obj",
label: "fruit",
fruits: ["apple", "banana", "tomato"]
}
}
}
<div data-tpl-with="parent.nested"><p>{{text}}</p></div>
renders to
<div><p>I'm the nested obj</p></div>
It is also possible to assing the evaluated valua to a variable using data-tpl-var
. This is useful for referencing it from another context.
<div data-tpl-with="parent.nested" data-tpl-var="nested">
<p>{{nested.text}}</p>
<div>
<h3>fruits</h3>
<p data-tpl-each="nested.fruits">{{nested.label}}: {{self}}</p>
</div>
</div>
that will render to
<div>
<p>I'm the nested obj</p>
<div>
<h3>fruits</h3>
<p>fruit: apple</p><p>fruit: banana</p><p>fruit: tomato</p>
</div>
</div>
data-tpl-each
Iterates over given array rendering the tag where the attribut is declared, for each array element. Sets the context to the current element.
data = {
a: [{v: 1}, {v: 2}, {v: 3}]
}
<div data-tpl-each="a">{{ v }}</div>
renders to
<div>1</div>
<div>2</div>
<div>3</div>
data-tpl-text
Evaluates the given expression and places it as text node inside the given element
data = {
text: "I'm so <i>pretty!</i>"
}
<p data-tpl-text="text"></p>
renders to
<p>I'm so <i>pretty!</i></p>
data-tpl-html
Evaluates the given expression and places it as inner html of the given element
data = {
text: "I'm so <i>pretty!</i>"
}
<p data-tpl-html="text"></p>
renders to
<p>I'm so <i>pretty!</i></p>
data-tpl-remove
Removes the tag, content or whole element where the attribute is specified
data-tpl-remove-tag
<div data-tpl-remove="tag"><p>paragraph</p></div>
renders to
<p>paragraph</p>
data-tpl-remove-body
<div data-tpl-remove="body"><p>paragraph</p></div>
renders to
<div></div>
data-tpl-remove-all
<div data-tpl-remove="all"><p>paragraph</p></div>
renders to
data-tpl-*
It is possible to preceed any attribute with data-tpl-
. It will evaluate the expression and set the result as value of an attribute having the name of the given data-tpl-
suffix
functions = {
text: {
concat: (separator, ...txt) => txt.join(separator)
}
}
data = {
color: "green"
}
<p data-tpl-style="#text:concat(' ', 'color:', color)">To be colored</p>
renders to
<p style="color: green">To be colored</p>
boolean values are rendered as boolean attributes
<input data-tpl-readonly="true">
renders to
<input readonly="readonly">
Expression evaluation
Object navigation
data = {
parent: {
nested: {text: "I'm the nested obj"}
}
}
<p data-tpl-text="parent.nested.text"></p>
renders to
<p>I'm the nested obj</p>
Nullsafe navigation
data = {
parent: {
empty: null
}
}
<p data-tpl-text="parent.empty?.text"></p>
renders to
<p></p>
Bracket notation navigation
data = {
today: "tue",
carToPick: {
mon: "Ferrari",
tue: "Lambo",
wed: "Porsche"
}
}
<p data-tpl-text="carToPick[today]"></p>
renders to
<p>Lambo</p>
Ternary operator
data = {
amIRich: false,
richCar: "Maserati",
poorCar: "Mazda"
}
<p data-tpl-text="amIRich ? richCar : poorCar"></p>
renders to
<p>Mazda</p>
Call a method
data = {
parent: {
nested: {
fruits: ["apple", "banana", "tomato"]
}
}
}
<p data-tpl-text="parent.nested.fruits.join(', ')"></p>
renders to
<p>apple, banana, tomato</p>
Evaluate self
The keyword self
refers to the current context
data = {
parent: {
nested: {
fruits: ["apple", "banana", "tomato"]
}
}
}
<p data-tpl-each="parent.nested.fruits">{{self}}</p>
renders to
<p>apple</p><p>banana</p><p>tomato</p>
Define and call a function
Call custom defined functions
functions = {
math: {
sum: (...addends) => addends.reduce((a,b) => a + b, 0)
}
}
<p data-tpl-text="#math:sum(1, 5, 3, 65)"></p>
renders to
<p>74</p>
Operators, literals and precedence
- Dict literals: e.g:
{"a": 1, "b": 2}
- Array literals: e.g:
[1,2,3]
- String literals: e.g:
"a"
or'a'
- Boolean literals: e.g:
true
orfalse
- Number literals: e.g:
3
or3.1
- Function calls: e.g:
#module:fn()
or#fn(1,2,3)
- Parenthesized expressions: e.g:
(a || b) && c
- Method call: e.g:
a.toLowerCase()
- Array access: e.g:
a[b]
ora?.[b]
- Member access: e.g:
a.b
ora?.b
- Logical not: e.g:
!a
- Relational operators: e.g:
a >= b
ora > b
ora <= b
ora <= b
- Equality operators: e.g:
a == b
ora != b
- Logical and expressions: e.g:
a && b
- Logical or expressions:e.g:
a || b
- Ternary operator: e.g:
a ? b : c