Search code examples
vue.jsvuejs2vue-directives

VueJS Custom directive + emit event


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


Solution

  • 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/[email protected]/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>