Tiny and supper simple way to bind data to DOM elements. Inspired by VueJS's templating system, which when combined with the Observables library, provides similar "auto-update-view-as-data-changes" (reactivity) functionality.
$ npm install --save @purtuga/dom-data-bind
import {render, allDirectives} from "dom-data-bind"
const data = {
firstName: "Paul"
};
const divBinder = render(`<div>{{ firstName }}</div>`, data, allDirectives);
// Insert data bound rendered templated instance into the document's body
document.body.appendChild(divBinder);
// Later....
data.firstName = "Tom";
divBinder.DomDataBind.setData(data);
// rendered DOM is no longer needed, stop its data watchers
// This is important in case your data is persisted beyond the life
// of the rendered element
divBinder.DomDataBind.destroy();
<script type="module">
import {render} from "//unpkg.com/@purtuga/dom-data-bind?module";
var binder = render(`<div>hello {{name}}!</div>`, { name: "paul" });
document.body.appendChild(binder);
</script>
render(htmlTemplate, data, directives) {
// returns DocumentFragment
}
The render()
function will return a DocumentFragment
with the given HTML template provided on input bound the the data provided. A list of directives should also be defined if your template uses any Directive outside of the Token interpolation.
IMPORTANT: When defining an array of directives, the order in which the directives are in (the
array
) is important. Any directive that manages the DOM elemnet should be at the beginning of the arrary. Example: by default, theEach
andIf
directives are the begning of theallDirectives
array (positions0
and1
respectively).
Tokens are defined using the double mustache (curly braces): {{ }}
. These will be evaluated within the context of the data
provided on input.
All directives start with the underscore character (_
). Example:
<div _if="visible" _on.click="sayHello">{{ label.clickme }}</div>
Usage:
<div _if="showDiv">...</div>
Conditionally render an element and it's bindings based on the truthy
of an expression.
Note: in order to hide these types of conditional elements on initial page rendering, the following CSS rule should be added to you page stylesheet:
[_if] { display: none; }
Usage:
<div _show="isVisible">...</div>
Conditionally show or hide an element (uses style.display
). Unlike _if
, this directive will process the element's children.
Note: in order to hide these types of conditional elements on initial page rendering, the following CSS rule should be added to you page stylesheet:
[_show] { display: none; }
Usage:
<div _class="{ 'make-element-red': transaction.atError }">...</div>
Conditionally toggle css class names on an element. Class names are defined in an object as the key
and it will be applied if the value of that key evaluates to truthy
and removed if it evaluates to falsey
.
Usage:
<div _style="{ 'background-color': divStyles.bgcolor }">...</div>
Similar to _class
but with element styles directly (style
attribute. Conditionally toggle css styles on an element.
Usage:
<div _attr.title="page1.title">...</div>
Bind a value to an Element's attribute. The attribute name is defined by using a period followed by the attribute name.
Usage:
<a _on.click="doClick">...</a>
<!-- or: just evaluate an expression -->
<a _on.click="isDone ? runClick($ev) : validateClick($ev)"></a>
Bind a function to a event or evaluate an expression. When evaluating an expression, the DOM event object is available as $ev
.
Usage:
<!-- Arrays -->
<div _each="item in itemsArray">{{ item }}</div>
<div _each="(item, index) in itemsArray">...</div>
<!-- Objects -->
<div _each="value in itemsObject" _key="value.id">{{ value.name }}</div>
<div _each="(value, key) in itemsObject">...</div>
Loop through a list using the HTML element as the template for each item.
For efficiency, each template item should have a unique id that can be used with the _key
directive. Expressions inside of an each
template can access parent context using $parent
or the $root
to access the root data.
Note: in order to hide these types of conditional elements on initial page rendering, the following CSS rule should be added to you page stylesheet:
[_each] { display: none; }
Usage:
<div _html="ele.markup"></div>
Bind HTML markup to the elements (sets its innerHTML
). WARNING: Inserting markup directly into the page can be dangerous and lead to XSS attacks.
A Custom Element constructor (base class) - src/DomDataBindElement.js
- is also provided which uses @purtuga/component-element
. This base class is not exported nor is it included in the built bundle, and must be import
'd explicitly in order to be used. @purtuga/component-element
must first be installed - installation of his library will not install it automatically.
The DomDataBindElement
will contain an instance member called state
which should be set component is Mounted and which changes automatically trigger a render lifecycle to be scheduled.
Ensure @purtuga/component-element
is installed
npm i @purtuga/component-element --save
import {DomDataBindElement} from "dom-data-bind/src/DomDataBindElement"
class MyEle extends DomDataBindElement {
didMount() {
this.state = {
msg: "Hello World"
}
}
render() {
return `<div>{{state.msg}}</div>`;
}
}