JSFiddle - React, Tailwind, and code Playground

by Lee Sun-Hyoup

HTML

<div id="app">
  <p>{{user.name}}</p>
  <p>{{user.email}}</p>
  <input v-ime-model="user.name">
  <input v-ime-model="user.email">
</div>

JavaScript

const modelableTagRE = /^input|textarea?$/

function assignByPath(obj, key, value) {
    let keys = key.split(".");
    let last = keys.pop();
    keys.forEach(key => {
        if(!obj[key])
            obj[key] = {};
        obj = obj[key];
    });

    obj[last] = value;
}

function getByPath(obj, key) {
    let keys = key.split(".");
    let last = keys.pop();
    keys.forEach(key => {
        if(!obj[key])
            obj[key] = {};
        obj = obj[key];
    });

    return obj[last]
}

Vue.directive('ime-model', {
  inserted (el, binding, vnode) {
  	if (!modelableTagRE.test(vnode.tag)) {
      console.log(
        `v-ime-model is not supported on element type: <${vnode.tag}>. `,
        vnode.context
      )
    }
  
  	function watchForExpression() {
      return vnode.context.$watch(binding.expression, function() {
        el.value = getByPath(vnode.context.$data, binding.expression)
      })
    }
  	var unwatch = watchForExpression()
    el.addEventListener('input', function(e) {
    	if (unwatch != null) {
        unwatch()
        unwatch = null
      }
      //vnode.context.$data[binding.expression] = el.value
      assignByPath(vnode.context.$data, binding.expression, el.value)
      if (unwatch == null) {
      	unwatch = watchForExpression()
      }
    })
    //el.value = vnode.context.$data[binding.expression]
    el.value = getByPath(vnode.context.$data, binding.expression)
  }
})

new Vue({
	el: '#app',
  data: {
  	user: {
    	name: 'Lee Sun-Hyoup',
    	email: 'kciter@naver.com'
    }
  }
})