Two-way data binding in vanilla JS (POC)
Francesco Esposito
Posted on February 2, 2019
As Front-end Engineers we mostly use libraries and/or frameworks to develop and maintain complex web apps, but what there is under the hood? Do you ask yourself that question? You don't!? Well, You should! ๐
In this post, I would like to extend the previous example to two-way data binding. ๐บ
Two-way data binding ๐ค
If you are not familiar with that concept, two-way data binding means that every change to the state is immediately propagated to the view (and vice-versa).
Check out the demo below for a quick example:
DEMO
Let's break it down
What do we need to have two-way data binding?
- A view, in our example HTML.
- A state, kept in memory with JavaScript.
The Key feature is:
Every time the state changes, the view needs to be updated (one-way data binding)
but also
Every time the view changes, the state needs to be updated
So let's assume we have an HTML view:
<div class="field">
<label for="name">Enter your name:</label>
<input id="name" type="text" name="name" data-model="name" />
</div>
<div class="field">
<label for="title">Enter your title:</label>
<input id="title" type="text" name="title" data-model="title" />
</div>
<div class="results">
<h1 data-binding="name"></h1>
<h2 data-binding="title"></h2>
</div>
and a state:
const state = {
name: 'Francesco',
title: 'Front-end Developer'
};
We can easily set the view the first time:
document.querySelector('[data-binding="name"]').innerHTML = state.name
document.querySelector('[data-binding="title"]').innerHTML = state.title
document.querySelector('[data-model="name"]').value = state.name
document.querySelector('[data-model="title"]').value = state.title
But we want some magic, so that when we update the state:
state.name = 'Richard'
state.title = 'Technical Lead'
the view should update too.
To achieve this, we could modify the default behaviour of the set
property for the state
object, so that other than update the state, it would also update our view.
One way to do that in JavaScript is using the Proxy Object:
const createState = (state) => {
return new Proxy(state, {
set(target, property, value) {
target[property] = value; // default set behaviour
render(); // updates the view every time the state changes
return true;
}
});
};
const state = createState({
name = 'Francesco'
title = 'Front-end Engineer'
});
With the power of the Proxy every time we update our state
, the render
function will be called.
A possible implementation of render
can be:
const render = () => {
document.querySelector('[data-binding="name"]').innerHTML = state.name;
document.querySelector('[data-binding="title"]').innerHTML = state.title;
document.querySelector('[data-model="name"]').value = state.name;
document.querySelector('[data-model="title"]').value = state.title;
};
We just miss the last little piece. Every time we modify the view, the state should change accordingly. We can obtain that adding an event listener to the inputs: ๐
const listener = (event) => {
state[event.target.dataset.model] = event.target.value;
});
document.querySelector('[data-model="name"]').addEventListener('keyup', listener);
document.querySelector('[data-model="title"]').addEventListener('keyup', listener);
And Voilรก! Now the trick is complete! ๐จโ๐ป
More generic implementation (POC) ๐
Posted on February 2, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.