Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Two-way data binding in vanilla JS (POC)
Francesco Esposito
Francesco Esposito

Posted on • Edited on

     

Two-way data binding in vanilla JS (POC)

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

Let's break it down

What do we need to have two-way data binding?

  • Aview, in our example HTML.
  • Astate, kept in memory with JavaScript.

The Key feature is:

Every time thestate changes, theview 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 HTMLview:

<divclass="field"><labelfor="name">Enter your name:</label><inputid="name"type="text"name="name"data-model="name"/></div><divclass="field"><labelfor="title">Enter your title:</label><inputid="title"type="text"name="title"data-model="title"/></div><divclass="results"><h1data-binding="name"></h1><h2data-binding="title"></h2></div>
Enter fullscreen modeExit fullscreen mode

and astate:

conststate={name:'Francesco',title:'Front-end Developer'};
Enter fullscreen modeExit fullscreen mode

We can easily set theview the first time:

document.querySelector('[data-binding="name"]').innerHTML=state.namedocument.querySelector('[data-binding="title"]').innerHTML=state.titledocument.querySelector('[data-model="name"]').value=state.namedocument.querySelector('[data-model="title"]').value=state.title
Enter fullscreen modeExit fullscreen mode

But we want some magic, so that when we update the state:

state.name='Richard'state.title='Technical Lead'
Enter fullscreen modeExit fullscreen mode

theview should update too.

To achieve this, we could modify the default behaviour of theset property for thestate object, so that other than update thestate, it would also update ourview.

One way to do that in JavaScript is using theProxy Object:

constcreateState=(state)=>{returnnewProxy(state,{set(target,property,value){target[property]=value;// default set behaviourrender();// updates the view every time the state changesreturntrue;}});};conststate=createState({name='Francesco'title='Front-end Engineer'});
Enter fullscreen modeExit fullscreen mode

With the power of the Proxy every time we update ourstate, therender function will be called.
A possible implementation ofrender can be:

constrender=()=>{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;};
Enter fullscreen modeExit fullscreen mode

We just miss the last little piece. Every time we modify theview, thestate should change accordingly. We can obtain that adding an event listener to the inputs: 😎

constlistener=(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);
Enter fullscreen modeExit fullscreen mode

AndVoilá! Now the trick is complete! 👨‍💻

More generic implementation (POC) 🌟

Top comments(8)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
tormodvm profile image
Tormod Vold Mikkelsen
  • Joined

Very helpful! Thanks a lot. :)
You have a typo in there:

    const state = createState({      name = 'Francesco'      title = 'Front-end Engineer'    });

Should be

    const state = createState({      name: 'Francesco',      title: 'Front-end Engineer'    });
CollapseExpand
 
anujjaryal profile image
Anuj jaryal
  • Joined

How extend it to include array of elements, like
I have in DOM:



< input name=product[1][quantity]>
and so on...

CollapseExpand
 
phoinixi profile image
Francesco Esposito
I'm a passionate front-end engineer 👨🏼‍💻 with expertise in JavaScript, TypeScript, and React. Currently building modern web experiences.
  • Location
    Zürich (Switzerland)
  • Education
    Master of Science in Software Engineering
  • Pronouns
    He/Him
  • Work
    Front-end Engineer
  • Joined

I would say that this is mostly a Proof of concept to show a possible implementation of two-way data binding, but for an app I would advice you to use a library (e.g. React)

CollapseExpand
 
beardedbry profile image
Brian
Web developer, I like building things
  • Location
    Dayton, Ohio
  • Joined

Thanks for the article. How would you expand on this?
Unless I am understanding it wrong, It looks like the render function updates everything that has a binding. Would you make it so that it only updates the bindings that had changes?

CollapseExpand
 
sushilkundu143 profile image
Sushil Kundu
  • Joined
• Edited on• Edited

Yes. You are right. Here is the updated code.

const createState = (state) => {
return new Proxy(state, {
set(target, property, value) {
target[property] = value; // default set behaviour
render(property); // updates the view every time the state changes
return true;
}
});
};

const state = createState({
name: '',
title: ''
});

const render = (property) => {
document.querySelector([data-model=${property}]).value = state[property];
};

const listener = (event) => {
const {type, value, dataset} = event.target;
state[dataset.model] = value;
};

document.querySelector('[data-model="name"]').addEventListener('keyup', listener);

document.querySelector('[data-model="title"]').addEventListener('keyup', listener);

CollapseExpand
 
itsjzt profile image
Saurabh Sharma
Fullstack Web developer.
  • Location
    Delhi, India
  • Work
    Fullstack Web Developer at Codeword Tech
  • Joined

Helpful article, thanks 😋

CollapseExpand
 
ajayadav09 profile image
ajayadav09
  • Joined

Great will try this on smaller projects.

CollapseExpand
 
mervinsv profile image
Mervin
I'm a passionate web and mobile developer
  • Location
    Philippines
  • Joined

Thanks for this info!

Maybe the next one would be a two-way binding for a list.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I'm a passionate front-end engineer 👨🏼‍💻 with expertise in JavaScript, TypeScript, and React. Currently building modern web experiences.
  • Location
    Zürich (Switzerland)
  • Education
    Master of Science in Software Engineering
  • Pronouns
    He/Him
  • Work
    Front-end Engineer
  • Joined

More fromFrancesco Esposito

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp