我正在尝试构建一个灵活的轮播控件,该控件允许内部内容元素强制更改幻灯片,以及轮播控件本身可以更改幻灯片
页面中的示例结构如下
<my-carousel>
<div class="slide">
<button @click="$emit('next')">Next</button>
</div>
</div class="slide">
<button @click="$emit('close')">Close</button>
</div>
</my-carousel>
我的轮播模板就像
<div class="carousel">
<div class="slides" ref="slides">
<slot></slot>
</div>
<footer>
<!-- other carousel controls like arrows, indicators etc go here -->
</footer>
</div>
和类似的脚本
...
created() {
this.$on('next', this.next)
}
...
访问幻灯片等没问题,但是使用$ emit无效,我似乎也找不到解决此问题的简单方法。
我希望组件无需使用即可轻松重用
答案 0 :(得分:9)
插槽是根据父组件范围编译的,因此,从插槽发出的事件将仅由模板所属的组件接收。
如果要在转盘和幻灯片之间进行交互,可以使用scoped slot代替,它可以将数据和方法从转盘公开到插槽。
假设轮播组件具有next
和close
方法:
轮播模板:
<div class="carousel">
<div class="slides" ref="slides">
<slot :next="next" :close="close"></slot>
</div>
<footer>
<!-- other carousel controls like arrows, indicators etc go here -->
</footer>
</div>
轮播示例用法:
<my-carousel>
<template slot-scope="scope">
<div class="slide">
<button @click="scope.next">Next</button>
</div>
</div class="slide">
<button @click="scope.close">Close</button>
</div>
</template>
</my-carousel>
答案 1 :(得分:1)
不可能监听所包含组件从广告位内容发出的事件。就您而言,<my-carousel>
无法收听事件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
查找单击了哪个按钮。在https://github.com/vuejs/vue/issues/4332和https://github.com/vuejs/vue/issues/4781
有一种更高级的方法可以做到这一点,那就是编写自定义渲染功能。将父级传递的点击处理程序包装到carousel
渲染函数中,然后将新函数传递给广告位内容。但这很少做,并且会认为它接近反模式。
答案 2 :(得分:1)
只需将$emit('next')
替换为$parent.$emit('next')
。
答案 3 :(得分:1)
简单方法
export default {
computed: {
defaultSlot() {
return this.$scopedSlots.default();
}
},
methods: {
this.defaultSlot.forEach(vnode => {
vnode.componentInstance.$on('someevent', (e) => {
console.log(e)
});
});
}
}
答案 4 :(得分:0)
只需创建一个事件侦听器组件(例如“ EventListener”),它所做的就是呈现默认的广告位,如下所示:
EventListener.vue
export default {
name: 'EventListener'
render() {
return this.$slots.default;
}
}
现在使用此<event-listener>
组件并将其包装在您的<slot>
上。插槽内的子组件应向父对象发出事件,例如:this.$parent.$emit('myevent')
。
将自定义事件附加到<event-listener @myevent="handleEvent">
组件。
<div class="carousel">
<event-listener @next="handleNext" @close="handleClose">
<div class="slides" ref="slides">
<slot></slot>
</div>
</event-listener>
<footer>
<!-- other carousel controls like arrows, indicators etc go here -->
</footer>
</div>
<my-carousel>
<div class="slide">
<button @click="$parent.$emit('next')">Next</button>
</div>
</div class="slide">
<button @click="$parent.$emit('close')">Close</button>
</div>
</my-carousel>
注意:<event-listener>
组件只能有一个子vnode。它不能是<slot>
,所以我们只是将其包装在div
上。
答案 5 :(得分:0)
我发现可以使用$ 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>
父组件:
<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>
但是,如果可能的话,请使用上面提到的作用域版位。
答案 6 :(得分:0)
选中scoped slot。假设您的轮播组件具有fnNext
和fnClose
方法:
轮播模板:
<div class="carousel">
<div class="slides" ref="slides">
<slot name="slide-ctrls" :events="{ fnNext, fnClose }"></slot>
</div>
<footer>
<!-- Other carousel controls like arrows, indicators etc go here -->
</footer>
</div>
轮播示例用法:
<my-carousel>
<template slot="slide-ctrls" slot-scope="{ events: { fnNext, fnClose } }">
<div class="slide">
<button @click="fnNext">Next</button>
</div>
<div class="slide">
<button @click="fnClose">Close</button>
</div>
</template>
</my-carousel>
或者,使用v-slot
(更简洁,最新的处理方式):
<my-carousel>
<template v-slot:slide-ctrls="{ events: { fnNext, fnClose } }">
<div class="slide">
<button @click="fnNext">Next</button>
</div>
<div class="slide">
<button @click="fnClose">Close</button>
</div>
</template>
</my-carousel>
以防万一,如果您希望看到更扩展的代码形式而不是es6
,尽管这似乎有些令人困惑,但它向您显示了事物在何处以及如何传递/使用。
<div class="carousel">
<div class="slides" ref="slides">
<slot name="slide-ctrls" :events="{ atClickNext: fnNext, atClickClose: fnClose }"></slot>
</div>
<footer>
<!-- Other carousel controls like arrows, indicators etc go here -->
</footer>
</div>
轮播示例用法:
<my-carousel>
<template v-slot:slide-ctrls="{ events: { atClickNext: handleClickNext, atClickClose: handleClickClose } }">
<div class="slide">
<button @click="handleClickNext">Next</button>
</div>
<div class="slide">
<button @click="handleClickClose">Close</button>
</div>
</template>
</my-carousel>
答案 7 :(得分:0)
我知道这是一篇较旧的帖子,但是,它在 Google 上的排名很好 - 所以我想我会详细说明我发现的解决方法。如果有更好的方法来实现这一点,我欢迎反馈。
为了解释解决方案,我将使用日历示例...
这是我的场景
通用日历 -> 日历年 -> 日历月 -> 日历日
在日历日内,有一个插槽(日历日单元格)允许父母呈现当天的自定义视图。此槽沿行向上传递到父“日历”组件。
在我的场景中,我有一个使用“日历”的可用性日历,并覆盖了组件可用性日历日中传递的日历日单元格。
availability-calendar-day 发出“available-date-selected”,在这种情况下,“calendar”不需要知道这个事件。在堆栈中,只有可用性日历组件需要使用它。
模板:
<template> <!-- availability-calendar -->
<calendar>
<template #calendar-day-cell>
<availability-calendar-day @available-date-selected="dateSelected">
脚本:
{
name: 'availability-calendar',
methods:
{
dateSelected(date) {
// ...
}
问题
来自可用性日历日的发射未达到可用性日历。这是因为它没有复制到“日历”堆栈中。 IE。发射仅发射到 'calendar-day' 组件(定义插槽)。
解决方案
这不是一个纯粹的解决方案,但是,它确实对我有用,我欢迎任何其他关于解决方法的评论。
鉴于在插槽模板中定义的组件接受来自声明组件的 props,我完全绕过了事件过程并将所需的方法作为函数传递到组件中。
使用前面的示例,模板现在看起来像这样:
<template> <!-- availability-calendar -->
<calendar>
<template #calendar-day-cell>
<availability-calendar-day :dateSelectedHandler="dateSelected">
然后,在 'availability-calendar-day' 内,方法从 this.$emit('available-date-selected') 更改为 this.dateSelectedHandler() em>。为了在 Typescript 编译组件中支持这一点,prop 被输入为 Function。