yev yev - 15 days ago 22
Javascript Question

Vue.js - Emit event from directive

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();
in directive if 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 fn = handlers[name].fn;

if (typeof fn === 'function') {
fn(data);
}
}
}

//Basic Directive
<script>
Vue.directive('foo', {
bind(el, binding, vnode) {
setTimeout(() => {
emit(vnode, 'bar');
}, 3000);
}
});
</script>

yev yev
Answer

So the solution I am using so far is (considering there were no answers so far):

In directive add method:

var emit = (vnode, name, data) => {
  var handlers = vnode.data.on;

  if (handlers && handlers.hasOwnProperty(name)) {
    var fn = handlers[name].fn;

    if (typeof fn === 'function') {
      fn(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); }
}