web-dev-qa-db-fra.com

Vue.js - Emet un événement de la directive

Est-il possible d'émettre un événement personnalisé de la directive in le composant auquel cette directive est attachée.

Je m'attendais à ce que cela fonctionne comme décrit dans l'exemple, mais ce n'est pas le cas.

Exemple: 

//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>

Des idées sur le sujet? Est-ce que je manque quelque chose?

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

Mise à jour 1: 

D'accord, j'ai constaté que si j'appelle vnode.data.on.bar.fn(); (ou fns() dans les dernières versions de Vue) dans la directive, cela déclenchera le gestionnaire d'événements bar

Mise à jour 2:

Solution temporaire:

  /*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>
12
euvl

Donc, la solution que j'utilise dans Vue 2+ (vu qu'il n'y avait pas de réponse jusqu'à présent):

Dans la directive, ajoutez la méthode: 

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);
  }
}

Et appelez ça comme ça:

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

Les avantages d'une approche:

1 Conservez le même style de code dans votre projet, ce qui signifie que chaque gestionnaire peut être déclaré en tant que
v-on:handler_name et être traité de manière significative (pour le développeur). D’autres solutions, comme l’envoi de callback en tant que paramètre, sont parfois déroutantes et ne sont pas évidentes sans fouiller dans la documentation/code.

2 L'utilisation du système d'événements intégré permet également de gérer avec élégance les objets d'événements. Par exemple, ce code fonctionnera parfaitement

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

MODIFIER:

En v2.1 +, vous pouvez utiliser cette liaison dans la directive interne:

vnode.context.$emit(eventname)
21
euvl

Votre solution ne fonctionnait pas pour moi. En effet, vnode.data.on était toujours indéfini

Ce qui a fonctionné pour déclencher un événement était

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

J'espère que cela t'aides.

4
Florian

Je sais que c'est un problème ancien, mais si quelqu'un a des problèmes avec cela et que cela ne fonctionne pas. Vous pouvez utiliser les événements d'événements personnalisés javascript.

    vue.directive('click',{bind(el, binding, vnode) {
        el.addEventListener('click', (e)=>{
            const event = new CustomEvent('customevent', {detail: {
                                                          custom: "data", 
                                                          can: "be", 
                                                          in: "detail property"}, bubbles: true});
            el.dispatchEvent(event);
        })
    }
})

maintenant je peux l'utiliser comme

<div v-click @customevent="func">hello world</div>

je n'ai pas à définir $event car la valeur par défaut est émise en tant que dernier paramètre. cet événement a une propriété detail, qui contient vos données personnalisées dans ce cas, cet objet: 

{custom: "data", 
 can: "be", 
 in: "detail property"}

src https://github.com/vuejs/vue/issues/7147

0
joowtjuh