I Just Need to Bind Some Data to DOM

That’s right.  All I need is to create a widget that allows me to bind the input data to the DOM template. I don’t need a framework, or an additional build step, or fancy features, or components – just need to bind the data to DOM and if data changes — DOM is automatically updated.  That is what DomDataBind library does.

In the age of JSX and vDom, when the need to bind data to a single reusable widget came up, I instead found myself building my own small and simple utility to do this job.  I know, crazy right? But it was not that hard and to be honest I wanted the challenge.  The library provides me with the similar functionally available in other more established libraries all while being supper tiny and thus ideal for use within modules that are framework agnostic. I also walked away from this experience thinking that perhaps the whole concept of maintaining an entire DOM representation  in memory as a JavaScript data structure might be overkill in most cases. That being said, I also now have a more profound appreciation for the level of performance optimizations that goes into many popular View libraries out there.

Here is what my little utility looks like in action:

import DomDataBind from "dom-data-bind"

const div = document.createElement("div");
div.innerHTML = "
<div>{{ firstName }}</div>
";

const data = { firstName: "Paul" };

// Bind data to DOM
const divBinder = DomDataBind.create(div, data);

// update data - automatically updates DOM
data.firstName = "John";

// later, when dom is removed, destroy the binder
divBinder.destroy();

Template Token Interpolation

The utility’s base functionality includes the interpolation of “tokens” that are evaluated as a JavaScript expression within the context of data. Tokens use the double curly braces (mustaches) syntax as seen above in {{ firstName }}  which when evaluated will return the firstName property value from the data object.  Token interpolation is supported only within the body of elements and not on html element attributes (Directives handle those – see below).

The core ability to react to data updates and in turn apply those to the DOM element is provided by my own observable-data library, which I recently wrote about (blog post). When DomDataBind.create is called, it will convert the data given on input to an observable structure which is then used to attach event listeners for changes associated with the values used from that structure.

Directives

Directives are special HTML element attributes that allow for data to be bound to normal attributes or provide for more advanced features. Directives start with _ followed by the directive name.  Example:

<div>
<div>{{ firstName }}</div>
<div _if="address">{{ address.street }}, {{ address.city }}</div>
</div>

In the above sample, you can see that the div containing the address is using the _if directive, which means that it will only be rendered if its value is truthy – in this case, if the data has a property called address.  There are several other directive available, like _style, _class, _loop, _show to name a few.

Use Only What is Needed

One of my goals was to ensure that this utility was minimalist in nature. The default export from the library contains all directive available, but not all of those might be needed depending on the usage. Remember: I started this utility because I had the need to have one single widget support data binding, and it did not need allot of fancy stuff.

So the Utility is really made up of several internal pieces – the primary one being the DomDataBind base constructor and then all of the directives. So if your widget only needs to do data interpolation, then the default export (which includes all Directives) is overkill and the base constructor should be used instead. Example:


// Import ONLY the base utility. Do this using the named export
// instead of importing the default
import { DomDataBind } from "dom-data-bind"

One of the directives I use the most is the _class, which allows me to toggle classes names on the element. To use only that directive, the following would be done:


import { DomDataBind, ClassDirective } from "dom-data-bind"

DomDataBind.directives.push(DomDataBind);

const div = document.createElement("div");
div.innerHTML = "
<div _class='colors'>{{ firstName }}</div>
;";

const data = {
    colors: {
        redText: false
    },
    firstName: "Paul"
};
const divBinder = DomDataBind.create(div, data);

// Turn text red by applying a css class named "redText"
data.colors.redText = true;

 


I use this utility within small self contained widgets to drive behaviors based on input parameters given to the Widget’s constructor. It simplifies my interactions with widgets by not having to call methods on those but rather just manipulate the data that is being help by the higher order logic of whatever app I’m working on.

For more on this utility, see the github repo: github.com/purtuga/dom-data-bind

Enjoy.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s