ANOTHER Sync in Vue 2.x with Mixin

by asemahle

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.js"></script>

<div id="app">
  <div v-for="(title, index) in titles">
    <p>Value in parent: {{title.title}}</p>
    <custom-component :value_sync="'titles[' + index + '].title'"></custom-component>
    <button v-on:click="title.title = 'Changed From Parent!'">
      CHANGE FROM PARENT
    </button>
  </div>
</div>

<template id="custom-component">
  <div>
    <p>Value in child: {{value}}</p>
    <button v-on:click="value = 'Changed From Child!'">
      CHANGE FROM CHILD
    </button>
  </div>
</template>

JavaScript

var Syncify = function(vueSettings) {
	//synced properties are managed within the component as computed properties
	if (vueSettings.computed == null) vueSettings.computed = {};
  
  //get all prop names
  var propNames = [];
  if (vueSettings.props == null) vueSettings.props = [];
  if (Object.prototype.toString.call( vueSettings.props ) === '[object Array]') 
  	propNames = vueSettings.props;
  if (Object.prototype.toString.call( vueSettings.props ) === '[object Object]')
  	propNames = _.keys(vueSettings.props);
    
  //only keep prop names that end in '_sync'
  propNames = propNames.filter((e) => { return e.endsWith('_sync'); });
  
  //add a computed property for each syncable prop
  for (let propName of propNames) {
  	//get <THIS-PART>_sync. (e.g., propName == cow_sync, then dataName == cow)
    dataName = propName.slice(0, -5);

    //do not override manually written setters/getters
    if (dataName in vueSettings.computed) continue;

    //set up computed property (setters and getters) referencing $parent
    vueSettings.computed[dataName] = {
      get: function() { return _.get(this.$parent, this[propName]); },
      set: function(v){ _.set(this.$parent, this[propName], v); },   
    };
  }
  
  //return modified settings
	return vueSettings;
}

Vue.component('custom-component', Syncify({
  template: '#custom-component',
  props: ['value_sync'],
}));

var app = new Vue({
    el: '#app',
    data: {
        titles: [
        	{ title: '1' },
          { title: '2' },
          { title: '3' },
        ]
    }
});