Currently, I am in the process of developing a dynamic tab menu using Vue 3 and slots. Successfully implementing tabs, I have created BaseTabsWrapper and BaseTab components. The challenge lies in using v-for with the BaseTab component inside a BaseTabsWrapper Component like so:
<section
id="content"
class="w-full mx-2 pr-2"
v-if="incomingChatSessions && incomingChatSessions.length"
>
<BaseTabsWrapper>
<BaseTab
v-for="chatSession in incomingChatSessions"
:key="chatSession.id"
:title="chatSession.endUser.name"
>
<p>{{ chatSession }}</p>
</BaseTab>
</BaseTabsWrapper>
</section>
An important note is that the incomingChatSessions object is asynchronous and sourced from a websocket. Tests confirm that this object works as intended and never returns empty.
In the BaseTabsWrapper template, key parts are:
<template>
<div>
<ul
class="tag-menu flex space-x-2"
:class="defaultTagMenu ? 'default' : 'historic'"
role="tablist"
aria-label="Tabs Menu"
v-if="tabTitles && tabTitles.length"
>
<li
@click.stop.prevent="selectedTitle = title"
v-for="title in tabTitles"
:key="title"
:title="title"
role="presentation"
:class="{ selected: title === selectedTitle }"
>
<a href="#" role="tab">
{{ title }}
</a>
</li>
</ul>
<slot />
</div>
</template>
The script includes:
<script>
import { ref, useSlots, provide } from 'vue'
export default {
props: {
defaultTagMenu: {
type: Boolean,
default: true,
},
},
setup(props) {
const slots = useSlots()
const tabTitles = ref(
slots.default()[0].children.map((tab) => tab.props.title)
)
const selectedTitle = ref(tabTitles.value[0])
provide('selectedTitle', selectedTitle)
provide('tabTitles', tabTitles)
return {
tabTitles,
selectedTitle,
}
},
}
</script>
This represents the Tab component template:
<template>
<div v-show="title === selectedTitle" class="mt-4">
<slot />
</div>
</template>
<script>
import { inject } from 'vue'
export default {
props: {
title: {
type: String,
default: 'Tab Title',
},
},
setup() {
const selectedTitle = inject('selectedTitle')
return {
selectedTitle,
}
},
}
</script>
A problematic segment in my script causing issues is:
const tabTitles = ref(
slots.default()[0].children.map((tab) => tab.props.title)
)
Despite fetching multiple title elements via API, upon page load, the array tabTitles only contains one element. Interestingly, triggering a re-render results in the correct number of elements in tabTitles and the corresponding tabs on the menu display correctly. While confirming data flow from the websocket to hydrate the "incomingChatSessions" array functions accurately, the issue persists where tabTiles remains limited to one element.