One-directional data binding without frameworks

  • 时间: 2018-05-12 11:57:00

This post is another by InRhythm’s own Jack Tarantino. For the full post and additional links, check out the original “Frameworkless JavaScript Part 3: One-Way Data Binding” on his website.

The following article is one in a series about writing client-focused JavaScript without the help of libraries and frameworks. It’s meant to remind developers that they can write good code on their own using nothing but native APIs and methods. For more, check out the original article on  writing small JavaScript components without frameworks and the previous article on  templates and rendering.

This article is intended to be a deep-dive into data-binding, how it works, and how you can do it without frameworks like Angular, React, or Ember. It is strongly recommended that you read the previous article before this one.

1-way data-binding

1-way data-binding is “a method of putting data into the DOM which updates whenever that data changes”. This is the major selling point of the React framework and with a little effort you can set up your own data binding with much less code. This is particularly useful when you have an application that sees routine changes to data like a simple game, a stock ticker, or a twitter feed; objects needs to have data pushed to the user but no user feedback is required. In this case, we need an object with some data in it:

let data_blob = {    movie: 'Iron Man',  quote: 'They say that the best weapon is the one you never have to fire.'}

A Proxy:

const quote_data = new Proxy(data_blob, {    set: (target, property, value) => {    target[property] = value    console.log('updated!')  }  })

And a poor DOM node to be our guinea pig:

<p class="js-bound-quote">My favorite {{ movie }} quote is "{{ quote }}".</p>

In this case, we need the data_blobto serve as a storage unit for the proxy. Proxies in ES6 are a convenient way to trigger callbacks when certain actions are taken on an object. Here, we’re using the proxy to trigger a callback every time somebody changes a value in the data blob. We don’t have a way to update the text in the DOM node yet though so let’s set that up:

const quote_node = document.querySelector('.js-bound-quote')quote_node.template = quote_node.innerHTML  quote_node.render = function render (data) {    this.innerHTML = this.template.replace(/\{\{\s?(\w+)\s?\}\}/g, (match, variable) => {    return data[variable] || ''  })}

This gives us a quick and dirty way to update the node’s inner HTML with some arbitrary data. The only thing needed to connect our new script with the proxy is to substitute the console.logcall with  quote_node.render(data_blob):

const quote_data = new Proxy(data_blob, {    set: (target, property, value) => {    target[property] = value    quote_node.render(data_blob)  }  })

With all this setup, we can add a quick script to prove that our DOM node is, in fact, updated every time we change the data blob. The exact same way that we want things to happen with a framework but with no external dependencies and WAYless code.

const quotes = [    "What is the point of owning a race car if you can't drive it?",  "Give me a scotch, I'm starving.",  "I'm a huge fan of the way you lose control and turn into an enormous green rage monster.",  "I already told you, I don't want to join your super secret boy band.",  "You know, it's times like these when I realize what a superhero I am."]window.setInterval(() => {    const quote_number = Math.floor(Math.random() * quotes.length)  quote_data.quote = quotes[quote_number]}, 2000)

This adds a script to change to a random quote every two seconds. Check out the working example below:

This is a little sloppy, as it only works for one node, one time. Let’s clean things up a bit and add constructors for both the nodes and Proxies. Continue reading on…