@optionfactory/ftl v2.0.9
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
DocumentFragmentor 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- are evaluated in the followind order:
data-tpl-ifdata-tpl-withdata-tpl-eachdata-tpl-whendata-tpl-valuedata-tpl-class-appenddata-tpl-attr-appenddata-tpl-textdata-tpl-htmldata-tpl-removedata-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 prefix 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>Special values
boolean values are always rendered as boolean attributes
<input data-tpl-readonly="true">renders to
<input readonly>null and undefined values cause the attribute to not be rendered
<input data-tpl-readonly="aNullValue">renders to
<input>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:
trueorfalse - Number literals: e.g:
3or3.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()ora.toLowerCase?.() - Array access: e.g:
a[b]ora?.[b] - Member access: e.g:
a.bora?.b - Logical not: e.g:
!a - Relational operators: e.g:
a >= bora > bora <= bora <= b - Equality operators: e.g:
a == bora != b - Logical and expressions: e.g:
a && b - Logical or expressions:e.g:
a || b - Ternary operator: e.g:
a ? b : c
8 months ago
8 months ago
9 months ago
6 months ago
6 months ago
6 months ago
7 months ago
8 months ago
8 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago