web-dev-qa-db-ja.com

Vue.js-ディレクティブからイベントを発行する

カスタムイベントを発行することは可能ですかfromディレクティブinこのディレクティブがアタッチされるコンポーネント。

例で説明したように動作することを期待していましたが、動作しません。

例:

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

この件について何か考えはありますか?何か不足していますか?

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

更新1:

さて、ディレクティブでvnode.data.on.bar.fn();(またはfns() in latest Vue versions)を呼び出すと、barイベントハンドラーがトリガーされることがわかりました。

更新2:

一時的な解決策:

_  /*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>
_
17
euvl

したがって、Vue 2+)で使用している解決策(これまでに回答がなかった場合):

ディレクティブのaddメソッド:

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

そしてそれをこのように呼びます:

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

アプローチの利点:

1プロジェクトで同じコードスタイルを維持します。つまり、すべてのハンドラーを次のように宣言できます。
v-on:handler_nameおよび(開発者にとって)意味のある方法で処理されます。パラメータとしてコールバックを送信するなど、他の解決策は時々混乱し、ドキュメント/コードを掘り下げない限り明白ではありません。

2組み込みのイベントシステムを使用すると、イベントオブジェクトを適切に処理することもできます。たとえば、次のコードは完全に正常に動作します。

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

編集:

V2.1 +では、この内部ディレクティブバインディングを使用できます。

vnode.context.$emit(eventname)
24
euvl

あなたの解決策は私のために働いていませんでした。実際、vnode.data.onは常に未定義でした

イベントをトリガーするために機能したのは

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

お役に立てれば。

3
Florian

上記の回答はすばらしいですが、一部は古くなっています。これらを実行可能なPOCに統合して問題を解決する方法は次のとおりです。

_// src/directives/ClickOutside.js
export default {
  stopProp(e) {
    e.stopPropagation();
  },
  bind(el, binding, vnode) {
    el._clickOutside = e => {
      vnode.context.$emit(binding.expression, e);
    };
    el.addEventListener('click', binding.def.stopProp);
    document.body.addEventListener('click', el._clickOutside);
  },
  unbind() {
    if (!el._clickOutside) {
      return;
    }
    el.removeEventListener('click', binding.def.stopProp);
    document.body.removeEventListener('click', el._clickOutside);
    delete el._clickOutside;
  }
};

// src/directives/index.js
import Vue from 'vue';
import ClickOutside from './ClickOutside';

Vue.directive('ClickOutside', ClickOutside);
_

Main.jsにディレクティブをインポートします。

_// src/main.js
import './directives';
_

Vueコンポーネントでイベント発行をリッスンしてディレクティブを使用します:

_// src/components/Component.vue
<template>
  <!-- Please fill in sensible context. This example doesn't really care about the DOM presentation -->
  <div @click="showElement" v-click-outside="hideElement">
    <div v-if="shouldShow">Hello</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      shouldShow: true
    };
  },
  mounted() {
    this.$on('hideElement', this.hideElement);
  },
  destroyed() {
    this.$off('hideElement', this.hideElement);
  },
  methods: {
    showElement() {
      this.shouldShow = true;
    },
    hideElement() {
      this.shouldShow = false;
    }
  }
};
</script>
_

基本的に、_vnode.context.$emit_では、_binding.expression_は、_v-close-outside_で宣言した文字列(この例では「hideElement」)です。ディレクティブからエミッションを取得するには、this.$on('hideElement')を使用してそれをリッスンします。

1
Fliss Hou

私はそれが古い問題であることを知っていますが、誰かがこれに問題があり、それが機能していない場合。 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);
        })
    }
})

今、私はそれを次のように使うことができます

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

デフォルトは最後のパラメータとして発行される標準であるため、$eventを設定する必要はありません。このイベントにはdetailプロパティがあり、この場合、このオブジェクトのカスタムデータが含まれています。

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

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

0
joowtjuh

カスタムのネイティブJavaScriptイベントを発行できます。 node.dispatchEventを使用して、ノードからイベントをディスパッチするディレクティブを作成します

let handleOutsideClick;
Vue.directive('out-click', {
    bind (el, binding, vnode) {

        handleOutsideClick = (e) => {
            e.stopPropagation()
            const handler = binding.value

            if (el.contains(e.target)) {
                el.dispatchEvent(new Event('out-click')) <-- HERE
            }
        }

        document.addEventListener('click', handleOutsideClick)
        document.addEventListener('touchstart', handleOutsideClick)
    },
    unbind () {
        document.removeEventListener('click', handleOutsideClick)
        document.removeEventListener('touchstart', handleOutsideClick)
    }
})

このように使用できます

h3( v-out-click @click="$emit('show')" @out-click="$emit('hide')" )
0
Pedro Torchio