Reputation: 61
I'm working on tab component and I want to render tab labels in parent component by getting child's slot, named 'label'
In Vue 2.x I could approach that, by referring to $slots
property of tab component, in Tabs.vue:
<template>
<section class="tabs">
<ul class="tabs-labels">
<li
v-for="tab in tabs"
:key="tab._uid"
:class="[{'active': tab.isActive}, 'tab-label']"
@click="selectTab(tab);"
>
{{ tab.$slots.label }}
</li>
</ul>
<div class="tabs-content">
<slot/>
</div>
</section>
</template>
<script>
export default {
name: 'Tabs',
data () {
return {
tabs: [],
};
},
mounted () {
// filter tabs in case there were additional vue components placed in slots
this.tabs = this.$children.filter(tab => tab.$options.name === 'TabContent');
},
methods: {
selectTab (selectedTab) {
// set isActive property of the tab by comparing their uids
this.tabs.forEach(tab => {
tab.isActive = (tab._uid === selectedTab._uid);
});
},
},
};
</script>
TabsContent.vue:
<template>
<div v-show="isActive" class="single-tab-content">
<slot/>
</div>
</template>
<script>
export default {
name: 'TabContent',
data () {
return {
isActive: false
};
},
};
</script>
Here, when the tab label clicked, in Tabs.vue
I iterate through tabs
array and setting their isActive
property, comparing their uid
and uid
of selectedTab
But in Vue 3.x API of slots has changed, so I changed the way of getting tab contents:
from
this.tabs = this.$children.filter(tab => tab.$options.name === 'TabContent');
to
this.tabs = this.$slots.default().filter(tab => tab.type.name === 'TabContent');
but as I understand, it getting only vNodes, not actual VueComponent that rendered, so when I'm executing selectTab
method
tab.isActive = (tab._uid === selectedTab._uid);
it updates only isActive
properties for tabs, that were saved in tabs
array, not for actual tab contents, so v-show
never changes.
Is there any way to get actual rendered VueComponents from <slots>
? Or maybe this approach is wrong from the beginning and I should try something else?
CodeSandboxes for both versions:
Upvotes: 0
Views: 1849
Reputation: 3440
Its a bit more complicated with Vue 3. You will want to look into using provide and inject. here is a good example. https://gist.github.com/cathrinevaage/4eed410b31826ce390153d6834909436
sandbox - https://codesandbox.io/s/happy-rubin-z414h?file=/src/App.vue
The example above is using typescript however you get the idea.
Upvotes: 1