(Under construction 🚧 )
TinyTemplatesJs is a tiny reactive template engine written in javascript. It embeds in standard HTML syntax using special DOM nodes that work as statements. These statements might be conditions or loops.
The HTML string template combined with the additional syntax nodes are displayed as the view in the DOM. The template can store data in form of state. The engine then keeps track of the state for any template and updates the DOM nodes accordingly if the data changes. diffDOM is used to detect these changes. It is able to spot differences between two node-lists and is able to patch these changes in as few steps as possible.
Let us create a small template as an example first, that works as a basic counter. It can increase and reset a number that is stored in its state and output its value on the screen. There will also be a reset button to set the number back to zero. If you want the full source code to follow along please have a look right here!
We start off by creating an instance of TinyTemplate which is the base class for any template.
The constructor of TinyTemplate looks like this:
constructor(name, state, methods, stringView);It takes a name that is used to uniquely identify the template, a state object that contains all the reactive data members, the methods object, which stores all class-methods and the html-like stringView.
Let us create an instance of TinyTemplate for our counter. We give it the name _counter_ and and then add a property to the state called number. This property is now a reactive state member of our template which means that every time this member is changed via setState(), the whole stringView gets reparsed and differences to the currrent view are written to the DOM. Now it is time to add some functionality. The methods-object takes functions so let's pass two of them:
increaseNumber: incrementnumberby one.reset: setsnumberback to zero.
Now we only have to create the stringView and then we should be good to go. It consists of one root node (!) with a fieldset inside that shows our number and a button for each of our two functions. To display our state value we use mustache syntax. To attach an onclick event to a node you should use the on-event and call attributes. You can use the standard html5 event attributes (e.g. onclick="") but those will lead to context problems if the template is not available inside the html file that renders this template (_this_ will not be available).
let counterTemplate = new TinyTemplate(
/* name */
"counter",
/* state */
{
number: 0
},
/* methods */
{
increaseNumber: function() {
this.changeState({ number: this.getState("number") + 1 });
},
reset: function() {
this.changeState({ number: 0 });
}
},
/* stringView */
`<div class="counter">
<fieldset>
<legend><b>Counter</b></legend>
<p>Counting: {{number}}</p>
<button on-event="click" call="increaseNumber">Increment</button>
<button on-event="click" call="reset">Reset</button>
</fieldset>
</div>`
);Lets instantiate our template and mount it to a root node (app in this instance):
counterTemplate.mount(document.getElementById("app"));Our basic template should now be visible in the DOM underneath the specified root-node. How all the parts work in detail is explained further below.
Double mustaches inside the stringView are replaced by their corresponding value. The parser first takes the value inside the mustaches and searches for it in the state-object, if it is not in the state it checks wether the value is a local variable and replaces it. It is also possible to access the properties of those variables:
<p>Access: {{stateObject}}</p>
<p>Access: {{localVariable}}</p>
<p>Access: {{localVariable.property}}</p>If you want to change any of the state variables (e.g. name), you need to call this.setState({name: 'NewName'}). This way the engine registers the specific changes to the data to only update the specific parts of the DOM that are affected by the change. If you change the state without the setState()-method, it wont update the DOM.
this.changeState({ number: this.getState("number") + 1 });The if statement only renders the containing nodes if the condition (expr) inside the braces (this.getState('payment') === 'visa') evaluates to true. The statement ends with a /fi.
<if expr="{{payment}} === 'visa'")>
<p>VISA payment used!</p> <!-- Only visible when the above statement becomes true. -->
</fi>Use for loops to render nodes multiple times. The syntax should be self explaining. The first attribute var declares the index variable, from and to define the range of the loop and step is used to determine the number by which the index variable increments each cycle. The index variable j can be accessed from anywhere inside the loop using the 'mustache' syntax.
<for var="j" from="0" to="{{max}}" step="2">
<p>Counting: {{ j }}</p>
</for>The foreach loop is the range based counterpart to the standard for loop we talked about before. It consists of four attributes of which at least two have to be provided. It is crucial to pass the elem attribute which contains the current list item (or contains the name of the variable that contains the current list item to be more precise) and the list itself, via in. The other two optional attributes are the index counter (idx) and arr, which contains the array itself.
<foreach elem="elem" idx="i" arr="arr" in="this.getState('animals')">
<div class="cart-product">{{fruit.product}}</div>
<div class="cart-price">{{fruit.price}}</div>
<br>
</foreach>Is is possible to nest every statement! The following...
<if expr="1==1">
<p>Depth 1</p>
<if expr="2==2">
<p>Depth 2</p>
<for var="i" from="0" to="3" step="1">
<foreach elem="elem" idx="idx" in="this.getState('animals')">
<p>[{{i}},{{idx}}] => {{elem.name}}</p>
</foreach>
</for>
</fi>
</fi>...converts to:
<span id="if_1">
<p>Depth 1</p>
<span id="if_2">
<p>Depth 2</p>
<span id="for_3">
<span id="for_4">
<p>[0, 0] => Tiger</p>
<p>[0, 1] => Shark</p>
<p>[0, 2] => Turtle</p>
</span>
<span id="for_5">
<p>[1, 0] => Tiger</p>
<p>[1, 1] => Shark</p>
<p>[1, 2] => Turtle</p>
</span>
<span id="for_6">
<p>[2, 0] => Tiger</p>
<p>[2, 1] => Shark</p>
<p>[2, 2] => Turtle</p>
</span>
</span>
</span>
</span>As you can see the statement nodes are replaced by a simple span.
Have fun building your own templates with tiny-templates-js.
