10

I need to $emit an event from a custom directive. Is it possible?

directive.js:

vnode.context.$emit("myEvent") // nothing append
vnode.child.$emit("myEvent")   // error
vnode.parent.$emit("myEvent")  // error

component.vue:

<div v-directive.modifier="binding" @myEvent="method()"></div>

Do you know if it's possible or if there is any trick?

Thanks

Adriano
  • 3,788
  • 5
  • 32
  • 53
Brice Chaponneau
  • 564
  • 2
  • 9
  • 22

1 Answers1

16

A <div> is not a VueComponent, which means it doesn't have an $emit method.

So to make your Vue custom directive emit an event, you will have to do some checking first:

  • If the directive was used in a Vue custom component, then call $emit() of that component's instance
  • If the directive was used in a regular DOM element (...because there's no $emit()...), then dispatch a native DOM event using .dispatchEvent.

Luckily, Vue's v-on listeners respond to native custom events.

That should be all. Demo implementation below.

Vue.component('my-comp', {
  template: `<input value="click me and check the console" size="40">`
});

Vue.directive('my-directive', {
  bind: function (el, binding, vnode) {

    // say you want to execute your logic when the element is clicked
    el.addEventListener('click', function (e) {
    
      var eventName = 'my-event';
      var eventData = {myData: 'stuff - ' + binding.expression}
      if (vnode.componentInstance) {
       vnode.componentInstance.$emit(eventName, {detail: eventData}); // use {detail:} to be uniform
      } else {
       vnode.elm.dispatchEvent(new CustomEvent(eventName, {detail: eventData}));
      }

    })
  }
})

new Vue({
  el: '#app',
  methods: {
    handleStuff(e) { console.log('my-event received', e.detail); }
  }
})
<script src="https://unpkg.com/vue@2.5.15/dist/vue.min.js"></script>

<div id="app">
  <div v-my-directive.modifier="'native element'" @my-event="handleStuff">CLICK ME AND CHECK THE CONSOLE</div>
  <hr>
  <my-comp v-my-directive.modifier="'custom component'" @my-event="handleStuff"></my-comp>
</div>
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • Thank you. but where is it write ? https://vuejs.org/v2/api/#vm-emit or https://vuejs.org/v2/guide/custom-directive.html. Plus, Do i need to stop propagation in div or component that use this directive with a prevent or stop ? – Brice Chaponneau Mar 14 '18 at 14:16
  • What do you mean by "where is write"? There's a demo above. No need to stop propagation, unless you have some requirement to specifically do it. If you don't know it is because you don't have it. – acdcjunior Mar 14 '18 at 14:32
  • Sorry, bad english :D. I have not seen anything in the documentation about "vnode.componentInstance" or the mechanism you describe to me. Your demonstration is very good thank you. – Brice Chaponneau Mar 14 '18 at 14:42
  • Ah, yeah, the `componentInstance` is "documented" in the interface: https://github.com/vuejs/vue/blob/dev/flow/vnode.js#L14 it is part of it. It is not an internal property because internal properties begin with a `_` (and don't appear in the interface). It is the same as the well-known `vnode.context`, guess there aren't many tutorials using it because it is an advanced feature of Vue. – acdcjunior Mar 14 '18 at 14:57
  • Ah OK. Thanks again. Do you know where I can find information on advanced features? I have needs vuex, real time store share by multi users ... Moreover, I just posted a question on vuex here: https://stackoverflow.com/questions/49280576/vuejs-vuex-mapactions – Brice Chaponneau Mar 14 '18 at 15:08