# TILEPIECES TEMPLATE (TT)

The constructor `window.TT` is an HTML template processor written in javascript that implements two-way data binding.

## parameters

element
A DOM element. The content of the element is will be processed
model
A javascript object that will serve as a model for our template
options
Here you can set statements names, interpolation regex and templates

## Return value

a tt object. The most important method is set

## Basic usage

        <div id=example>${data}</div>
        <script>
        new TT(document.getElementById("example"),{data:"some data"},{ // here the default values
            interpolation : /\$\{([\s\S]+?)\}/,
            ifAttr : "if",
            foreachAttr : "foreach",
            foreachKeyNameAttr : "foreachKeyName",
            bindAttr : "bind",
            bindDOMPropAttr : "bindDomProp",
            bindEventsAttr : "bindEvents",
            setTemplateAttr : "set",
            useTemplateAttr : "use",
            useTemplateParamsAttr : "params",
            isolateAttribute : "isolate",
            srcAttribute : "src"
        });
        </script>

## Interpolation

Interpolation allows to define node texts and attributes values and names ( when setting attributes names, remember that it will changed to lowercase ).

    <div id="example">
      <a href="${URL}" ${hidden}>${text}</a>
    </div>
    <button onclick="changeHidden()">Change hidden value</button>
    <script>
      let model = {
        URL : "https://github.com/tilepieces",
        text : "some text",
        hidden : "hidden"
      };
      let template = new TT(document.getElementById("example"),model);
      function changeHidden(){
        template.set("hidden",model.hidden ? "" : "hidden");
      }
    </script>

See the live example

## Statements

Statements are defined by data attributes

### data-if

tells the parser to render or not ( the dom element will be removed ) a tag.
The attribute value is a variable that will be coerced to a boolean.

    <div id=example>
      <div data-if="show">This will be shown</div>
      <div data-if="hide">This will be hidden</div>
    </div>
    <button id="toggle-visibility">Toggle visibility</button>
    <script src="../tt.js" data-tilepieces-component="tt"></script>
    <script>
      let scope = {
          show:true,
        hide:false
      };
      let template = new TT(document.getElementById("example"),scope);
      document.getElementById("toggle-visibility").onclick = ()=>{
        scope.show = !scope.show;
        scope.hide = !scope.hide;
        template.set("",scope);
      };
    </script>

See the live example

### data-foreach

Tells the parser to render a tag n-times according to an array of objects.
You can retrieve object property using `key` or setting the `data-foreach-key-name` attribute in the same tag ( best for nested arrays ).

    <ul>
      <li data-foreach="array1">
        <div>Name <span>${key.name}</span></div>
        <div>Value <span>${key.value}</span></div>
      </li>
    </ul>
    <ul>
      <li data-foreach="array2" data-foreach-key-name="prop">
        <div>Name <span>${prop.name}</span></div>
        <div>Value <span>${prop.value}</span></div>
        <ul>
        <li data-foreach="prop.array" data-foreach-key-name="propNested">
        <div>Name <span>${propNested.name}</span></div>
        <div>Value <span>${propNested.value}</span></div>
      </li>
    </ul>
    </li>
    </ul>
    <script>
    let array1 = [{
      name: "array1_1",
      value: 10
    },
    {
      name: "array1_2",
      value: 100
    }];
    let array2 = [{
      name: "array2_1",
      value: 0,
      array : [{
        name: "array2_1_0_nested",
        value: 0
      }]
    },
    {
      name: "array2_2",
      value: 10,
      array : [{
        name: "array2_2_0_nested",
        value: 1
      },{
        name: "array2_2_0_nested",
        value: 2
      }]
    }]
    let scope = {array1, array2};
    let template = new TT(document.body,scope);
    </script>

See the live example

### data-bind and data-bind-events

Allows you to link the data model to user changes through the `input`, `select`, `textarea` tags and with the `contenteditable` attribute. By default it is linked with the change event, but you can override this behavior by defining the data-bind-events tag (which accepts multiple values separated by a comma).

    <section>
        <input placeholder="name"
               data-bind="user.name"
               data-bind-events="input">
        <input placeholder="surname"
               data-bind="user.surname"
               data-bind-events="input">
        <fieldset>
            Male<input type="radio" data-bind="user.sex" name="user.sex" value="M"><br>
            Female<input type="radio" data-bind="user.sex" name="user.sex" value="F">
        </fieldset>
        <fieldset>
            <input type="checkbox" data-bind="user.employed">
        </fieldset>
        <fieldset data-if="user.employed">
            <select data-bind="user.job">
                <option value="developer">developer</option>
                <option value="designer">designer</option>
                <option value="manager">manager</option>
                <option value="hr">hr</option>
                <option value="other">other</option>
            </select>
        </fieldset>
        <h1 data-if="!user.name && !user.surname">Please fill inputs</h1>
        <h1 data-if="user.name || user.surname">
            Hello ${user.name} ${user.surname}!</h1>
        <pre>
        </pre>
    </section>
    <script>
        let scope = {
            user  : {
                name : "",
                surname : "",
                sex:"M",
                employed : true,
                job : "developer"
            }
        };
        let section = document.querySelector("section");
        let template = new TT(section,scope);
        let pre = document.querySelector("pre");
        section.addEventListener("user.name",e=>{
            console.log("user.name detail ->", e.detail)
        })
        section.addEventListener("user.surname",e=>{
            console.log("user.surname detail ->", e.detail)
        })
        section.addEventListener("template-digest",e=>{
            console.log("template-digest ->", e.detail)
            pre.innerHTML = JSON.stringify(scope,null,4);
        });
    </script>

See the live example

### data-bind-dom-prop

Attach a variable as a property of a DOM element

    <div onclick="alertData(this)" data-bind-dom-prop="__data,data" style="cursor:pointer">Click here for an alert with the data value</div>
    <input data-bind="data" placeholder="change data here">
    <script>
      var scope = {data:""};
      var template = new TT(document.body,scope);
      function alertData(el) {
        console.log(el);
        alert(el.__data);
      };
    </script>

See the live example

### data-set, data-use and data-params

Bind an HTML template ( that could be defined through data-set or in the options values ) to a tag.
`data-params` is used pass variables using another name.
Example using `data-set` ( See the live example )

    <section>
        <div data-set="test-template">
            <span>${obj.name}</span>:
            <span>${obj.value}</span>
        </div>
        <ul>
            <li data-foreach="data"
                data-use="test-template"
                data-params="obj,key"></li>
        </ul>
    </section>
    <script>
        let scope = {
            data : [{
                name : "name1",
                value : "value1"
            },{
                name : "name2",
                value : "value2"
            }]
        }
        let el = document.body.children[0];
        let t = new TT(el,scope);
    </script>

Example using the template tag ( See the live example )

    <section>
        <ul>
            <li data-foreach="data"
                data-use="test-template"
                data-params="obj,key"></li>
        </ul>
    </section>
    <template id="test-template">
        <span>${obj.name}</span>:
        <span>${obj.value}</span>
    </template>
    <script>
        let scope = {
            data : [{
                name : "name1",
                value : "value1"
            },{
                name : "name2",
                value : "value2"
            },{
                name : "name3",
                value : "value3"
            }]
        };
        let template = document.getElementById("test-template");
        let el = document.body.children[0];
        let t = new TT(el,scope,{
            templates : [{
                name : "test-template",
                el : template.content
            }]
        });
    </script>

### data-src

Used in `frame` and `img` tags to dynamically set them without letting the browser download template placeholders
( See the live example )

### data-isolate

Isolate part of the HTML. This part will not be processed.

    <section>
       <div>
           <span>${name}</span>:
           <span>${value}</span>
       </div>
       <div data-isolate>
           <div>This element is isolated:</div>
           <span>${name}</span>:
           <span>${value}</span>
       </div>
    </section>
    <script>
       let scope = {
           name : "an",
           value : "example"
       }
       let el = document.body.children[0];
       let t = new TT(el,scope);
    </script>

See the live example

## set

The `set` method allows to update the model. You can pass the property to be changed as a string (the original object will also be updated), but if you need to make more than one change it is better to pass the whole object.

    <section>
        ${name}<br>
        ${nestedExample.name}
        <ul>
            <li data-foreach="array">
                ${key.name}
            </li>
        </ul>
    </section>
    <script>
        let scope = {
            name : "an example",
            nestedExample : {
                name : "an example2"
            },
            array : [
                {name : "an example3"}
            ]
        }
        let el = document.body.children[0];
        let t = new TT(el,scope);
        let o = "an example changed";
        t.set("name",o);
        console.assert(scope.name == o);
        let o2 = "an example2 changed";
        t.set("nestedExample.name",o2);
        console.assert(scope.nestedExample.name == o2);
        let o3 = "an example3 changed";
        t.set("array[0].name",o3);
        console.assert(scope.array[0].name == o3);
    </script>

See the live example