简体   繁体   English

Vue.js - 从指令发出事件

[英]Vue.js - Emit event from directive

Is it possible to emit a custom event from the directive in the component to which this directive is attached.是否有可能从与该指令被附着组件的指令发出一个自定义事件。

I was expecting it to work as described in example, but it does not.我期待它像示例中描述的那样工作,但事实并非如此。

Example:示例:

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

Any ideas on the matter?关于此事的任何想法? Am I missing something?我错过了什么吗?

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

Update 1:更新 1:

Okay, i've found that if I call vnode.data.on.bar.fn();好的,我发现如果我调用vnode.data.on.bar.fn(); (or fns() in latest Vue versions) in directive it will trigger bar event handler. (或最新 Vue 版本中的fns() )在指令中它会触发bar事件处理程序。

Update 2:更新 2:

Temporary solution:临时解决方案:

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

So the solution I am using in Vue 2+ (considering there were no answers so far):所以我在 Vue 2+ 中使用的解决方案(考虑到目前还没有答案):

In directive add method:在指令添加方法中:

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

And call it this way:并这样称呼它:

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

The benefits of an approach:一种方法的好处:

1 Keep the same code-style in your project, meaning that every handler can be declared as 1 在您的项目中保持相同的代码风格,这意味着每个处理程序都可以声明为
v-on:handler_name and be handled in meaningful (for developer) way. v-on:handler_name并以有意义的(对于开发人员)方式处理。 Other solutions, like sending callback as parameter, are sometimes confusing and not obvious without digging into documentation/code.其他解决方案,例如将回调作为参数发送,如果不深入研究文档/代码,有时会令人困惑且不明显。

2 Using built-in events system also allows to gracefully handle event objects. 2 使用内置事件系统还可以优雅地处理事件对象。 For example, this code will work perfectly fine:例如,此代码将完美运行:

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

EDIT:编辑:

In v2.1+ you can use this inside directive binding:在 v2.1+ 中,您可以在指令绑定中使用它:

vnode.context.$emit(eventname)

Your solution was not working for me.您的解决方案对我不起作用。 Indeed vnode.data.on was always undefined事实上 vnode.data.on 总是未定义的

What worked to trigger an event was触发事件的方法是

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

Hope this helps.希望这会有所帮助。

I know it is an old issue, but if someone has problems with this and it is not working.我知道这是一个老问题,但是如果有人对此有问题并且无法正常工作。 You can use use javascript custom events events.您可以使用 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);
        })
    }
})

now i can use it like现在我可以像这样使用它

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

i do not have to set $event because the default is standard emitted as last param.我不必设置$event因为默认值是作为最后一个参数发出的标准。 this event has a detail property, which contains your custom data in this case this object:这个事件有一个detail属性,在这种情况下它包含你的自定义数据这个对象:

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

src https://github.com/vuejs/vue/issues/7147源代码https://github.com/vuejs/vue/issues/7147

The above answers are great but some of them are out-of-date.上面的答案很好,但其中一些已经过时了。 Here's the way I solve the problem by integrating them into a workable POC.这是我通过将它们集成到一个可行的 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);

Import the directives in main.js:在 main.js 中导入指令:

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

Use the directive with listening to the event emission in a Vue component:使用指令来监听 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>

Basically, in vnode.context.$emit , the binding.expression is the string (ie, "hideElement" in this example) you declared in the v-close-outside .基本上,在vnode.context.$emit ,该binding.expression是字符串(即,在这个例子中“hideElement”),你在宣布v-close-outside To retrieve the emission from the directive, use this.$on('hideElement') to listen to it.要从指令中检索发射,请使用this.$on('hideElement')来收听它。

You can emit custom native javascript events.您可以发出自定义本机 javascript 事件。 Create a directive that dispatches an event from the node, using node.dispatchEvent使用 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)
    }
})

Which can be used like this哪个可以这样使用

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

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

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

@euvl's solution is fine, but i think it's easier and cleaner to just pass a function as an argument to your directive. @euvl 的解决方案很好,但我认为将函数作为参数传递给指令更容易、更清晰。 Seems to simplify the interface to your directive as well.似乎也简化了指令的接口。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM