'Vue3 pass component as prop
i'm building a tab component where i need to pass component as a prop.
how can i display the component passed in the props?
Tab.vue
<template>
<div class="bg-white flex items-center rounded-tr rounded-tl shadow border-b border-grey overflow-hidden">
<button @click="activeTab = title" v-for="title in titles" v-text="title"
class="font-medium px-4 py-3 hover:bg-grey hover:bg-opacity-70 border-b-2 border-transparent hover:border-primary"
:class="title === activeTab && 'border-primary bg-grey bg-opacity-70 '"/>
</div>
<div class="bg-white p-4 min-h-[calc(100vh-10rem)]">
<component :is="activeTab"></component> // not working
</div>
</template>
<script setup>
import {onMounted, ref} from "vue";
const props = defineProps({
titles: {},
tabs: {}
})
const activeTab = ref("")
onMounted(() => {
activeTab.value = props.titles[0]
})
</script>
Settings.vue
<template>
<Tab :titles="[$t('languagesLabel'),$t('countriesLabel')]" :tabs="[Languages,Countries]">
</Tab>
</template>
<script setup>
import Tab from "../../components/admin/common/Tab.vue";
import Countries from "../../components/admin/settings/Countries.vue";
import Languages from "../../components/admin/settings/Languages.vue";
</script>
the question is how to make the displayed component dynamic ?
Solution 1:[1]
You could use slot
to pass components instead of props :
<div class="bg-white p-4 min-h-[calc(100vh-10rem)]">
<slot/>
</div>
in Settings.vue :
<template>
<Tab :titles="[$t('languagesLabel'),$t('countriesLabel')]" >
<Countries />
<Languages />
</Tab>
</template>
<script setup>
import Tab from "../../components/admin/common/Tab.vue";
import Countries from "../../components/admin/settings/Countries.vue";
import Languages from "../../components/admin/settings/Languages.vue";
</script>
Solution 2:[2]
When using <script setup>
, the <component>.is
prop needs to be set to the component definition itself (not the component's name).
One solution is to track the active index (instead of active tab component):
Change
activeTab
to be a computed prop that returns the current tab fromtabs[]
based on the active index.Create an
activeIndex
ref, initialized to0
.In the
v-for
, include theindex
iteratorIn the
button
'sclick
handler, set theactiveIndex
to the currentindex
.In the
class
binding, compare the currentindex
toactiveIndex
.
<script setup>
import { onMounted, ref, computed } from 'vue'
const props = defineProps({
titles: {},
tabs: {},
})
1??
const activeTab = computed(() => props.tabs[activeIndex.value])
2??
const activeIndex = ref(0)
</script>
<template>
<div class="bg-white flex items-center rounded-tr rounded-tl shadow border-b border-grey overflow-hidden">
<button
3??
v-for="(title, index) in titles"
4??
@click="activeIndex = index"
v-text="title"
class="font-medium px-4 py-3 hover:bg-grey hover:bg-opacity-70 border-b-2 border-transparent hover:border-primary"
5??
:class="index === activeIndex && 'text-red border-primary bg-grey bg-opacity-70 '"
/>
</div>
<div class="bg-white p-4 min-h-[calc(100vh-10rem)]">
<component :is="activeTab"></component>
</div>
</template>
Also, make sure to call markRaw()
on the component definitions in the binding to avoid the unnecessary overhead of reactivity. This also resolves a Vue warning about this in the browser console.
<template> ? ?
<Tab :titles="['Languages', 'Countries']" :tabs="[languages, countries]"> </Tab>
</template>
<script setup>
import { markRaw } from 'vue'
import Tab from '@/components/Tab.vue'
import Countries from '@/components/Countries.vue'
import Languages from '@/components/Languages.vue'
?
const countries = markRaw(Countries)
const languages = markRaw(Languages)
</script>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Boussadjra Brahim |
Solution 2 | tony19 |