Vanilla.js Data-Binding

How to implement data-binding with plain old JavaScript

by Jeremy Likness

HTML

First name:
<input id="firstName" type="text" />
<br/> Last name:
<input id="lastName" type="text" />
<br/> Age:
<input id="age" type="number" />
<br/>
<p id="output">
  Fill out the form!
</p>
<button id="reset">
  Reset
</button>

JavaScript

(function() {

  const inpFirstName = document.getElementById('firstName'),
    inpLastName = document.getElementById('lastName'),
    inpAge = document.getElementById('age'),
    pOutput = document.getElementById('output'),
    btnReset = document.getElementById('reset');

  const onPropertyChangedFn = (callbackList, propertyName) => {
    callbackList.forEach(cb => cb(propertyName));
  }

  const observeDOM = (elem, eventName, fn) => {
    elem.addEventListener(eventName, () => fn(elem));
  };

  class ViewModel {

    constructor() {
      this.cbList = [];
      this._firstName = '';
      this._lastName = '';
      this._age = null;
    }

    registerPropertyChange(cb) {
      this.cbList.push(cb);
    }

    get firstName() {
      return this._firstName;
    }

    set firstName(val) {
      if (val !== this._firstName) {
        this._firstName = val;
        onPropertyChangedFn(this.cbList, 'firstName');
      }
    }

    get lastName() {
      return this._lastName;
    }

    set lastName(val) {
      if (val !== this._lastName) {
        this._lastName = val;
        onPropertyChangedFn(this.cbList, 'lastName');
      }
    }

    get age() {
      return this._age;
    }

    set age(val) {
      if (Number(val) !== this._age) {
        this._age = Number(val);
        onPropertyChangedFn(this.cbList, 'age');
      }
    }
  }

  var vm = new ViewModel();

  observeDOM(inpFirstName, 'blur', inp => vm.firstName = inp.value);
  observeDOM(inpLastName, 'blur', inp => vm.lastName = inp.value);
  observeDOM(inpAge, 'keyup', inp => vm.age = inp.value);

  observeDOM(btnReset, 'click', () => {
    vm.firstName = '';
    vm.lastName = '';
    vm.age = 0;
    inpFirstName.focus();
  });

  vm.registerPropertyChange(prop => {
    if (prop === 'firstName') {
      inpFirstName.value = vm.firstName;
    }
    if (prop === 'lastName') {
      inpLastName.value = vm.lastName;
    }
    if (prop === 'age') {
      inpAge.value = vm.age;
    }
    const txt = 'Hi,...