Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js - Emit event from directive

Tags:

Is it possible to emit a custom event from the directive in the component to which this directive is attached.

I was expecting it to work as described in example, but it does not.

Example:

//Basic Directive <script>   Vue.directive('foo', {     bind(el, binding, vnode) {       setTimeout(() => {         //vnode.context.$emit('bar'); <- this will trigger in parent         vnode.$emit('bar');       }, 3000);     }   }); </script>   //Basic Component <template>   <button v-foo @bar="change">{{label}}</button> </template> <script>   export default{     data() {       return {         label: 'i dont work'       }     },     methods: {       change() {         this.label = 'I DO WORK!';       }     }   } </script> 

Any ideas on the matter? Am I missing something?

JSFiddle: https://jsfiddle.net/0aum3osq/4/

Update 1:

Okay, i've found that if I call vnode.data.on.bar.fn(); (or fns() in latest Vue versions) in directive it will trigger bar event handler.

Update 2:

Temporary solution:

  /*temp. solution*/   var emit = (vnode, name, data) => {     var handlers = vnode.data.on;      if (handlers && handlers.hasOwnProperty(name)) {       var handler = handlers[name];       var fn = handler.fns || handler.fn;        if (typeof fn === 'function') {         fn(data);       }     }   }   //Basic Directive <script>   Vue.directive('foo', {     bind(el, binding, vnode) {       setTimeout(() => {         emit(vnode, 'bar');       }, 3000);     }   }); </script> 
like image 911
euvl Avatar asked Nov 17 '16 12:11

euvl


People also ask

How do I pass a Vue event?

To pass event and argument to v-on in Vue. js, we can call the event handler method with $event and whatever argument. And then we can retrieve the arguments from the method parameters in the same order. to call addToCart with the $event object and the ticket.id property.

What is this emit in Vue?

Vue $emit is a function that lets us emit, or send, custom events from a child component to its parent. In a standard Vue flow, it is the best way to trigger certain events.


2 Answers

So the solution I am using in Vue 2+ (considering there were no answers so far):

In directive add method:

var emit = (vnode, name, data) => {   var handlers = (vnode.data && vnode.data.on) ||     (vnode.componentOptions && vnode.componentOptions.listeners);    if (handlers && handlers[name]) {     handlers[name].fns(data);   } } 

And call it this way:

bind(el, binding, vnode) {   emit(vnode, 'bar' , {some: 'event', data: 'here'}); } 

The benefits of an approach:

1 Keep the same code-style in your project, meaning that every handler can be declared as
v-on:handler_name and be handled in meaningful (for developer) way. Other solutions, like sending callback as parameter, are sometimes confusing and not obvious without digging into documentation/code.

2 Using built-in events system also allows to gracefully handle event objects. For example, this code will work perfectly fine:

<button v-foo @bar="bar(1, $event, 2)">{{label}}</button> ... methods: {   bar(one, event, two) { console.log(one, event, two); } }  

EDIT:

In v2.1+ you can use this inside directive binding:

vnode.context.$emit(eventname) 
like image 64
euvl Avatar answered Sep 17 '22 09:09

euvl


Your solution was not working for me. Indeed vnode.data.on was always undefined

What worked to trigger an event was

 vnode.child.$emit('myevent'); 

Hope this helps.

like image 35
Florian Avatar answered Sep 21 '22 09:09

Florian