Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue directive to detect changes in model

Tags:

vue.js

vuejs2

How can I write a Vue 2.x directive in such a way that it can detect changes in the model? I can only bind to the element and detect input, keydown, etc. But i can't detect when the model was updated. Is this out of scope for Vue's directives?

 Vue.directive('text-validation', {
        bind: function (el, binding, vnode) {
            el.addEventListener('input', function(){
            	console.log('only gets called on input, not model updates');
            });
        }
    });
    
new Vue({
	el: '#app',
  data: {
  	text: 'testing...'
  },
  mounted: function() {
  	setTimeout(function(){
       this.text = 'detected change';
    }.bind(this), 2000)
  }
})    
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.js"></script>

<div id="app">
  <input v-model="text" v-text-validation=""/>
</div>
like image 695
Elijah Avatar asked Mar 08 '23 11:03

Elijah


2 Answers

Ah, I forgot what the update hook is for. I created a working snippet, that does what I intended - an update on model calls the update hook

 Vue.directive('text-validation', {
        bind: function (el, binding, vnode) {
            el.addEventListener('input', function(){
            	console.log('got called');
            });
        },
        update: function(el, binding, vnode) {
        	console.log('got called on upadate');
        }
    });
    
new Vue({
	el: '#app',
  data: {
  	text: 'testing...'
  },
  mounted: function() {
  	setTimeout(function(){
       this.text = 'detected change';
    }.bind(this), 2000)
  }
})    
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.9/vue.js"></script>
<div id="app">
  <input v-model="text" v-text-validation=""/>
</div>

EDIT

I ended up setting up a watch() inside the bind() hook. Firing any kind of DOM native events from inside of update() was causing all sorts of infinite loops.

Pseudocode:

var modelExp = vnode.data.directives.find(d->d.name === 'model');
vnode.context.$watch(modelExp, function(){//do what i need}, {deep, true});

This was borrowed from "VeeValidate" project, ListenerGenerator.prototype._attachModelWatcher

like image 61
Elijah Avatar answered Mar 17 '23 15:03

Elijah


As @Bert pointed out - you can/could use watchers for that (if you do not need something more advanced - as central state / store Vuex etc.).

With watchers - it is very important to note that you can use them with "deep: true," that watches children inside objects;

watch: {
    myData: {
      handler: function (newVal, oldVal) {
        // we have new and old values
      },
      deep: true /* we will be notified of changes also if myData.child is changed :) */
    }
  }

State is more complicated but can be a saviour if app is getting more and more complex...

Found this usefull and simple demo: Vue - Deep watching an array of objects and calculating the change?

like image 44
nettutvikler Avatar answered Mar 17 '23 15:03

nettutvikler