Two-way data binding in vanilla JS (POC)
Tags: showdev, javascript, webdev, databinding
Published: 2019-02-02 14:44:02

---
title: Two-way data binding in vanilla JS (POC)
published: true
description: Proof-of-concept of two-way data binding in vanilla JS
tags: showdev, javascript, webdev, databinding
cover_image: https://thepracticaldev.s3.amazonaws.com/i/kdecdxfa97m9pmadegoc.jpeg
---

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

![demo]
(//thepracticaldev.s3.amazonaws.com/i/xax8qjn8qubpijnv2c3o.gif)

### 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**:
```html
    <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**:
```javascript	
    const state = {
      name: 'Francesco',
      title: 'Front-end Developer'
    };
```
		
We can easily set the **view** the first time:
```javascript
    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:
```javascript	
    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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy):
```javascript
    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:
```javascript
    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: 😎

```javascript
    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) 🌟

{% stackblitz two-way-data-binding-poc %}