Vue.js - 从指令发出事件
Posted
技术标签:
【中文标题】Vue.js - 从指令发出事件【英文标题】:Vue.js - Emit event from directive 【发布时间】:2017-04-01 00:30:06 【问题描述】:是否可以发出自定义事件从指令在该指令附加到的组件。
我希望它能够按照示例中的说明工作,但事实并非如此。
例子:
//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();
(或最新 Vue 版本中的 fns()
),它将触发 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>
【问题讨论】:
【参考方案1】:所以我在 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)
【讨论】:
效果很好!您的发出函数调用缺少结束括号。我试图编辑,但 SO 说我必须编辑 6 个字符。vnode.context.$emit()
在 vue@v2.5 下的这个例子中不起作用,如何解决?但是 emit 函数仍然运行良好。
根据我的经验,您只能在发送事件后调用vnode.context.$emit('name')
...element.addEvenetListener('click', () => vnode.context.$emit('handleClick'); );
有效!但是在事件触发器之外做一个简单的vnode.context.$emit('handleClick')
实际上并没有正确调用发射,因此你无法监听它。我可能是错的,但这就是我刚刚在 vue@2.5 中所经历的。
只是我,还是父级发出事件,而不是带有指令的实际元素?
这可以解释为什么它没有从正确的元素发出它。谢谢!【参考方案2】:
您的解决方案对我不起作用。事实上 vnode.data.on 总是未定义的
触发事件的原因是
vnode.child.$emit('myevent');
希望这会有所帮助。
【讨论】:
给了我未定义的;vnode.context.$emit
至少是 Vue v.2.1.10 中的一个函数
vnode.child.$emit('bar'); Vue v2.4.2 的 $emit 未定义。 jsfiddle.net/Jimsan/vuv80dpn/7【参考方案3】:
我知道这是一个老问题,但如果有人对此有问题并且它不起作用。您可以使用使用 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】:上面的答案很好,但其中一些已经过时了。这是我通过将它们集成到可行的 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')
来收听它。
【讨论】:
【参考方案5】:最简单的方法就是像这样在el
上使用dispatchEvent
el.dispatchEvent(new Event('change'));
【讨论】:
【参考方案6】:您可以发出自定义的原生 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')" )
【讨论】:
【参考方案7】:@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>
【讨论】:
恐怕这不能回答问题。另外,例如,如果您有指令v-draggable
,并且您希望有事件on-drag-start
、on-drag-end
、on-drag
、on-drag-error
,我认为您的方法不会那么干净。好电话。
@euvl 我想这有点取决于指令(OP 不是很具体)。我想你也可以给回调一个参数,但是你正在以某种方式重新构建事件,所以你的解决方案可能会更干净。以上是关于Vue.js - 从指令发出事件的主要内容,如果未能解决你的问题,请参考以下文章