Vue.js - 来自指令的Emit事件

时间:2016-11-17 12:47:55

标签: javascript vue.js vue-component

是否可以从 指令附加到该指令的组件中发出自定义事件

我希望它能像示例中所描述的那样工作,但事实并非如此。

示例:

vnode.data.on.bar.fn();

关于此事的任何想法?我错过了什么吗?

JSFiddle:QProcess::startDetached()

更新1:

好的,我发现如果我在指令中调用fns()(或最新的Vue版本中的bar),它将触发 /*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> 事件处理程序。

更新2:

临时解决方案:

RequestBody

7 个答案:

答案 0 :(得分:22)

所以我在Vue 2+中使用的解决方案(考虑到目前为止没有答案):

在指令中添加方法:

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)

答案 1 :(得分:4)

您的解决方案对我不起作用。确实vnode.data.on总是未定义

触发事件的方法是

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

希望这有帮助。

答案 2 :(得分:1)

以上答案很好,但其中一些已经过时。通过将它们集成到可行的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')进行监听。

答案 3 :(得分:0)

我知道这是一个老问题,但是如果有人对此有疑问并且无法正常工作。您可以使用使用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

答案 4 :(得分:0)

您可以发出自定义的本地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')" )

答案 5 :(得分:0)

最简单的方法是像这样在el上使用dispatchEvent

el.dispatchEvent(new Event('change'));

答案 6 :(得分:-1)

@ euvl的解决方案很好,但我认为将函数作为参数传递给你的指令更容易,更简洁。似乎也简化了指令的接口。

<script>
  Vue.directive('foo', {
    bind(el, binding) {
      setTimeout(() => {
        binding.value();
      }, 3000);
    }
  });
</script>

<template>
  <button v-foo="change">{{label}}</button>
</template>

<script>
  export default{
    data() {
      return {
        label: 'i dont work'
      }
    },
    methods: {
      change() {
        this.label = 'I DO WORK!';
      }
    }
  }
</script>