Vue.js 将事件从插槽中的内容发射到父级

Vue.js 将事件从插槽中的内容发射到父级,vue.js,vuejs2,vue-component,Vue.js,Vuejs2,Vue Component,我正在尝试构建一个灵活的旋转木马控件,允许内部内容元素强制更改幻灯片,以及旋转木马控件本身更改幻灯片 我的页面中的示例结构如下所示 <my-carousel> <div class="slide"> <button @click="$emit('next')">Next</button> </div> <div class="slide"> <button @click="$emit('c

我正在尝试构建一个灵活的旋转木马控件,允许内部内容元素强制更改幻灯片,以及旋转木马控件本身更改幻灯片

我的页面中的示例结构如下所示

<my-carousel>
  <div class="slide">
    <button @click="$emit('next')">Next</button>
  </div>

  <div class="slide">
    <button @click="$emit('close')">Close</button>
  </div>
</my-carousel>
访问幻灯片等没有问题,但是使用
$emit
将不起作用,我似乎无法找到解决此问题的简单方法

我希望组件能够轻松地重用,而无需使用

  • 中央活动巴士
  • 传送带内的硬编码幻灯片
  • 在页面级别实现下一个幻灯片方法,并将当前索引传递给控件(因为每次使用旋转木马时都必须这样做)

无法侦听所包含组件从插槽内容发出的事件。在您的情况下,
无法侦听事件
next
close
根据父组件范围编译插槽内容。

作为解决方法,您可以执行以下操作:

<div class="carousel">
    <!-- Listen to click event here -->
    <div class="slides" @click="doSomething($event)" ref="slides">
        <slot></slot>
    </div> 
    <footer>
        <!-- other carousel controls like arrows, indicators etc go here -->
    </footer>
</div>

doSomething
中,您可以使用
$event.target
找到单击的按钮。有关此问题的更多信息,请访问和


还有一种更高级的方法可以做到这一点,那就是编写自定义渲染函数。将父级传递的单击处理程序包装到
carousel
render函数中,并将新函数传递给槽内容。但是,这是非常罕见的事情,并且会考虑它接近反模式。

时隙是针对父组件范围编译的,因此,从槽中发出的事件将只由模板所属的组件接收。

如果您想在旋转木马和幻灯片之间进行交互,可以使用一个允许您将数据和方法从旋转木马公开到插槽的方法

假设您的转盘组件具有
next
close
方法:

旋转木马模板:


转盘示例用法:


下一个
接近
我的解决方案 只需创建一个事件监听器组件(例如“EventListener”),它所做的只是呈现默认插槽,如下所示:

EventListener.vue

现在使用此
组件并将其包装在
上。插槽中的子组件应该向父组件发出事件,如下所示:
this.$parent.$emit('myevent')

将自定义事件附加到
组件

转盘模板:

转盘示例:

下一个
接近

注意:组件必须只有一个子vnode。它不能是
,所以我们只是将它包装在
div
上。

我发现这可以使用$root完成

<h1>Regular html document content</h1>
<parent-component>
  <h2>Some parent html that goes inside the slot</h2>
  <child-component></child-component>
</parent-component>
常规html文档内容
插槽中的某些父html
父组件:

<template>
    <div>
        <slot></slot>
        <h3>extra html that is displayed</h3>
    </div>
</template>
<script>
export default {

    created() {
        this.$root.$on('child-event', this.reactOnChildEvent);
    },

    methods: {
        this.reactOnChildEvent: function(message) {
            console.log(message);
        }
    }
};
</script>
<template>
    <div>
      <button @click="$root.$emit('child-event', 'hello world')">
         click here
      </button>
    </div>
</template>

显示的额外html
导出默认值{
创建(){
this.root.on('child-event',this.reactionChildevent);
},
方法:{
this.reactionChildEvent:函数(消息){
控制台日志(消息);
}
}
};
子组件:

<template>
    <div>
        <slot></slot>
        <h3>extra html that is displayed</h3>
    </div>
</template>
<script>
export default {

    created() {
        this.$root.$on('child-event', this.reactOnChildEvent);
    },

    methods: {
        this.reactOnChildEvent: function(message) {
            console.log(message);
        }
    }
};
</script>
<template>
    <div>
      <button @click="$root.$emit('child-event', 'hello world')">
         click here
      </button>
    </div>
</template>

点击这里
但是,如果可能,请使用上面提到的作用域插槽。

只需将
$emit('next')
替换为
$parent。$emit('next')

简单方法

export default {
    computed: {
        defaultSlot() {
            return this.$scopedSlots.default();
        }
    },
    methods: {
       this.defaultSlot.forEach(vnode => {
           vnode.componentInstance.$on('someevent', (e) => {
              console.log(e)
           });
                
       });
    }
}
检查。假设您的转盘组件具有
fnext
fnClose
方法:

旋转木马模板:


转盘示例用法:


下一个
接近
或者,使用
v-slot
(更干净、最新的做事方式):


下一个
接近
以防万一,如果您希望看到更多扩展的代码形式,而不是
es6
,尽管这看起来有点混乱,但这会向您显示传递/使用内容的位置和方式


转盘示例用法:


下一个
接近

这似乎很不灵活,因为我不知道旋转木马中有哪些按钮(可能还有其他组件有很多按钮,它们可以做不同的事情)。我需要提供常量或按钮上附加的东西,以确定应该发生什么-对吗?@FrankProvost,不幸的是,这就是它的工作原理。过去一年半以来,我们将Vue.js用于相当大的应用程序。我们很少遇到这个要求。如果你不喜欢这个,有其他的方法,但它不会有你需要的自由结构。此外,考虑注入功能作为一个道具或使用范围和多个槽的组合。这有任何不利之处-它似乎很好地工作只是一个抬头,如果你使用包装组件,而不是像上面的例子一样简单按钮,可能,你需要一个V-on =“$听众”。若要转发所有事件侦听器。@mejiamanuel57如果需要有多个侦听器,可以将一个道具传递到包含对象的插槽中,该对象的所有事件名称都作为键,它们应该作为值运行。例如:
:on=“{input:onInput,click:onClick}”
,其中onXxxxx是包装器组件中的方法。然后,您可以使用作用域插槽和指令
v-on=
将侦听器及其各自的处理程序分配给插槽中的组件(
)。Vuetify也可以做到这一点。我不知道为什么这个答案没有更多的UPs!谢谢,对我来说效果很好。很好的答案!!!不幸的是,答案在这里被低估了。不费吹灰之力就解决了这个问题
<template>
    <div>
        <slot></slot>
        <h3>extra html that is displayed</h3>
    </div>
</template>
<script>
export default {

    created() {
        this.$root.$on('child-event', this.reactOnChildEvent);
    },

    methods: {
        this.reactOnChildEvent: function(message) {
            console.log(message);
        }
    }
};
</script>
<template>
    <div>
      <button @click="$root.$emit('child-event', 'hello world')">
         click here
      </button>
    </div>
</template>
export default {
    computed: {
        defaultSlot() {
            return this.$scopedSlots.default();
        }
    },
    methods: {
       this.defaultSlot.forEach(vnode => {
           vnode.componentInstance.$on('someevent', (e) => {
              console.log(e)
           });
                
       });
    }
}