-
Notifications
You must be signed in to change notification settings - Fork 39
Template
- Basic usage
- Passing data to templates
- Template as a table row and multiple insert
- Tree view and adjacency list
- Template handlers
- Template events, data parsing/escaping and transformation
Template base object used for adding sections of HTML to document and attaching variables and handlers to it.
Template contents should be inside any block element with id = "template_..." (template wrapper). For example:
<div id="template_user_block">
<div class="app-block-user">
<div class="app-block-user-header"></div>
<div class="app=block-user-body">
<img src="" alt="Profile photo" class="app-block-user-img">
<p class="app-block-user-about"></p>
</div>
</div>
</div>
Templates first should be defined with Template.define(template_id)
. When template is defined contents of wrapper are removed and placed into document fragment so there is no need of hiding templates.
It is recommended to add all templates at the bottom of the page.
Templates can be inserted into document with Template.insert(template_id, data = {}, parent_el)
. Second argument data can be omitted by passing empty object {} or empty array [] but not any other value. Third argument parent_el
should be a valid document node. Example:
import { Template } from 'bunnyjs/src/bunny.template';
Template.define('template_user_block');
document.addEventListener('DOMContentLoaded', function(e) {
Template.insert('template_user_block', [], document.querySelector('.container'));
}, false);
Data in templates can be placed in tag contents (innerHTML) or in attribute value.
For tag contents add attribute v="data_key"
to this element. v stands for variable. For attribute value add attribute av="attribute_name:data_key"
. av stands for attribute variable. Example:
<div id="template_user_block">
<div class="app-block-user">
<div class="app-block-user-header" v="name"></div>
<div class="app=block-user-body">
<img src="" alt="Profile photo" class="app-block-user-img" av="src:photo_path">
<p class="app-block-user-about" v="about"></p>
</div>
</div>
</div>
Now data can be placed into template with:
var data = {
name: 'John',
photo_path: '/img/john.png',
about: 'User description'
};
Template.insert('template_user_block', data, document.querySelector('.container'));
If template is for table row then template wrapper should be table tag because browsers will remove tr contents if not inside table. Example:
<table id="template_user_row">
<tr>
<td v="name"></td>
<td><img src="" alt="Profile photo" av="src:profile_photo"></td>
<td v="about"></td>
</tr>
</table>
To insert same template many times Template.insertAll(template_id, data_collection, parent_el)
should be used. It is not recommended to insert templates into DOM one by one. insertAll() method will iterate through data_collection which should be an array of data and create single document fragment and will made only one real DOM manipulation at the end.
var data_collection = [
{
name: 'John',
photo_path: '/img/john.png',
about: 'User description'
},
{
name: 'Oliver',
photo_path: '/img/oliver.png',
about: 'User 2 description'
}
];
Template.insertAll('template_user_row', data_collection, document.querySelector('.container_table'));
For example, you need to put comments, replies and so on in tree view. This is possible with Template.insertAdjacencyList(tpl_id, data_adj_list, parent_el, parent_id_column, branch_container_class, order)
...
Let say you have a button in form and need to attach onClick event on this button when template is inserted into DOM.
This is done by adding data-handler="function_name"
to button in template. Data handler function should be defined and passed as a 2nd argument when defining a template with Template.define(tpl_id, handlers = {})
where handlers is an object where keys are function_names
and values - callbacks. Callbacks will receive 3 arguments: first will be an element to which data-handler attribute was added. In our case - to button. However, data-handlers can be placed anywhere inside template. Second - escaped_data and third - template_nodes (All root nodes and their child inside template wrapper, usually it's single div or tr). Example:
<div id="template_popup">
<div class="app-popup">
<div class="app-popup-body">
...
</div>
<div class="app-popup-footer">
<button class="app-btn" data-handler="popup_close">Close</button>
</div>
</div>
</div>
Template.define('template_popup', {
popup_close: function(el, escaped_data, tpl_nodes) {
el.addEventListener('click', function(e) {
// remove popup from screen by deleting all inserted nodes
tpl_nodes.forEach(function(node) {
node.parentNode.removeChild(node);
});
});
}
});
The third and last argument can be passed to Template.define() is object of template events. They can be used for example on template init or to modify data before rendering. To do so use:
Template.define(tpl_id, {}, {
format_data: function(data) {
data.name = data_first_name + ' ' + data.last_name;
data.created_at = // modified data.created_at with custom format, for example.
return data; // don't fortget to return modified data
}
});
Each template event name has constant, for example, instead of format_data - TEMPLATE_EVENT_FORMAT_DATA can be used. format_data event is fired BEFORE template is parsing/escaping data.
Template automatically parses data and escapes HTML.
It is recommended to store data as is in database and only parse and escape it before rendering in browser. So Template is saving from XSS and there is no need to worry about it. Template also replaces new lines with br tag. So basicly no work on data is needed on backend.