Reputation: 155
I was wondering how can you make a parent component aware of child components inside a slot.
For example if I want to create a slider and I would like to have the following structure:
On my view:
<vue-slider>
<vue-slide name="First slide!">First slide content</vue-slide>
<vue-slide name="Second slide!">Second slide content</vue-slide>
</vue-slider>
VueSlider.vue
<template>
<div class="slider">
<ol>
<li v-for="(slide,index) in slides">{{slide.name}}</li>
</ol>
<slot></slot>
</div>
</template>
<script>
export default {
data: function(){
return {
slides: [],
}
}
}
</script>
VueSlide.vue
<template>
<div class="slide">
<slot></slot>
</div>
</template>
<script>
export default {
props: {
'name': {
}
}
}
</script>
So I want to populate the slides array on the parent component. I was considering emiting an event when every slide is mounted but then I read this on the vue docs:
You cannot use $on to listen to events emitted by children. You must use v-on >directly in the template, as in the example below.
Thanks!
Upvotes: 1
Views: 396
Reputation: 82499
Everything in the default slot is available through $slots.default
inside the component (named slots are available through $slots[name]
). So, if you wanted to populate the slides
array in the parent component, you could examine the contents of the default slot and eliminate nodes that are not slides (whitespaces like carriage returns are included as nodes).
Here is an example.
console.clear()
Vue.component("VueSlider", {
template: `
<div class="slider">
<h1>Number of slides: {{slides.length}}</h1>
<ol>
<li v-for="(slide,index) in slides">{{slide.name}}</li>
</ol>
<slot></slot>
</div>
`,
data: function() {
return {
slides: [],
}
},
mounted(){
for (let node of this.$slots.default){
// filter out whitespace nodes
if (node.tag){
// may want to check to add only VueSlide nodes
this.slides.push(node.componentInstance)
}
}
}
})
Vue.component("VueSlide", {
template: `
<div class="slide">
<slot></slot>
</div>
`,
props: {
'name': String
}
})
new Vue({
el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<vue-slider>
<vue-slide name="First slide!">First slide content</vue-slide>
<vue-slide name="Second slide!">Second slide content</vue-slide>
</vue-slider>
</div>
You'll want to examine the node
object so that you can see what the properties are. In this case, I'm pushing the componentInstance
into the slides array because that is where the data properties of the current component instance are located (so you can use {{slide.name}}
in the parent. In cases where I am doing this, however, I typically push a custom object into the collection that only has the information I want. Like so:
this.slides.push({name: node.componentInstance.name})
Upvotes: 1