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>
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.
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.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With