I'm just learning Vue and trying to learn dynamic rendering using the ponent tag. What is the problem with this code? No errors are displayed in the console, but clicking on the buttons still does not display the required ponent. It does work with v-if, but the point of this lecture I'm following is to use the ponent dynamic rendering. Which does not work:
<template>
<div>
<the-header></the-header>
<button @click="setSelectedComponent('active-goals')">Active goals</button>
<button @click="setSelectedComponent('manage-goals')">Manage goals</button>
<ponent :is="selectedTab"></ponent>
</div>
</template>
<script setup>
/* eslint-disable no-unused-vars */
import { ref, defineExpose, defineComponent } from 'vue';
import TheHeader from './ponents/TheHeader.vue';
import ActiveGoals from './ponents/ActiveGoals.vue';
import ManageGoals from './ponents/ManageGoals.vue';
const selectedTab = ref('active-goals');
const setSelectedComponent = (tab) => {
selectedTab.value = tab;
};
defineExpose({
selectedTab,
setSelectedComponent,
});
defineComponent({
ponents: {
TheHeader,
ActiveGoals,
ManageGoals,
},
});
</script>
<style>
html {
font-family: sans-serif;
}
body {
margin: 0;
}
</style>
Thanks for any help!
I'm just learning Vue and trying to learn dynamic rendering using the ponent tag. What is the problem with this code? No errors are displayed in the console, but clicking on the buttons still does not display the required ponent. It does work with v-if, but the point of this lecture I'm following is to use the ponent dynamic rendering. Which does not work:
<template>
<div>
<the-header></the-header>
<button @click="setSelectedComponent('active-goals')">Active goals</button>
<button @click="setSelectedComponent('manage-goals')">Manage goals</button>
<ponent :is="selectedTab"></ponent>
</div>
</template>
<script setup>
/* eslint-disable no-unused-vars */
import { ref, defineExpose, defineComponent } from 'vue';
import TheHeader from './ponents/TheHeader.vue';
import ActiveGoals from './ponents/ActiveGoals.vue';
import ManageGoals from './ponents/ManageGoals.vue';
const selectedTab = ref('active-goals');
const setSelectedComponent = (tab) => {
selectedTab.value = tab;
};
defineExpose({
selectedTab,
setSelectedComponent,
});
defineComponent({
ponents: {
TheHeader,
ActiveGoals,
ManageGoals,
},
});
</script>
<style>
html {
font-family: sans-serif;
}
body {
margin: 0;
}
</style>
Thanks for any help!
Share Improve this question edited Mar 27, 2023 at 16:24 Boussadjra Brahim 1 asked Mar 27, 2023 at 12:01 mrcrazyogmrcrazyog 171 silver badge5 bronze badges 1-
Move the ponent imports and their registration into a separate
<script>
tag, as shown in my answer. Details here. If the ponents are not registered, you can't use strings in:is
to reference them. The alternative is to use the ponent themselves instead of strings. – tao Commented Mar 27, 2023 at 14:46
4 Answers
Reset to default 6You can't use strings with :is
if the ponents are not registered.
You either use the ponents themselves instead of the strings (@Tolbxela's or @Bussadjra's answers), or register the ponents.
And you can't register the ponents (easily) in <script setup>
, because <script setup>
syntax is limited. It's literally a macro to write:
<script>
import { defineComponent } from 'vue'
export default defineComponent({
setup() {
// CODE HERE
}
})
as:
<script setup>
// CODE HERE
</script>
... with some mods to allow import
s inside the setup()
function and few more other goodies (defineProps
, defineEmits
, etc...).
The limitation should be obvious: if you need anything besides the contents of setup()
function from defineComponent()
, you want to use a normal <script>
.
In other words, <script setup>
should be treated as a tool to reduce boilerplate, you shouldn't put effort into attempting to write all your ponents using it.
Last, but not least, <script setup>
can be used alongside a normal <script>
tag. In your case:
<script setup>
const selectedTab = ref('active-goals');
const setSelectedComponent = (tab) => {
selectedTab.value = tab;
};
</script>
<script>
import { defineComponent } from 'vue'
import TheHeader from './ponents/TheHeader.vue';
import ActiveGoals from './ponents/ActiveGoals.vue';
import ManageGoals from './ponents/ManageGoals.vue';
export default defineComponent({
ponents: {
TheHeader,
ActiveGoals,
ManageGoals,
},
});
</script>
The normal <script>
registers the ponents so <template>
can translate the strings into actual ponents.
In script setup the imported ponents are treated as variables because they're not registered under keys as we do in ponents
option in non script setup syntax, so you can map them with keys inside an object :
<template>
<div>
<the-header></the-header>
<button @click="setSelectedComponent('ActiveGoals')">Active goals</button>
<button @click="setSelectedComponent('ManageGoals')">Manage goals</button>
<ponent :is="selectedTab"></ponent>
</div>
</template>
<script setup>
/* eslint-disable no-unused-vars */
import { ref, defineExpose, defineComponent } from 'vue';
import TheHeader from './ponents/TheHeader.vue';
import ActiveGoals from './ponents/ActiveGoals.vue';
import ManageGoals from './ponents/ManageGoals.vue';
const selectedTab = ref(ActiveGoals);
const ponents = {
ActiveGoals,
ManageGoals,
}
const setSelectedComponent = (tab) => {
selectedTab.value = ponents[tab];
};
defineExpose({
selectedTab,
setSelectedComponent,
});
</script>
for this to work you can register your ponents globally, for Vue to make the link with your selected ponent. Otherwise, you can use the defineAsync method from Vue.
registerGlobally
defineAsync
1: your .vue
<template>
<div>
<the-header></the-header>
<button @click="setSelectedComponent(ActiveGoals)">Active goals</button>
<button @click="setSelectedComponent(ManageGoals)">Manage goals</button>
<ponent :is="selectedTab"></ponent>
</div>
</template>
<script setup>
/* eslint-disable no-unused-vars */
import { ref, defineExpose, defineComponent } from 'vue';
import TheHeader from './ponents/TheHeader.vue';
const selectedTab = ref('active-goals');
const setSelectedComponent = (tab) => {
selectedTab.value = tab;
};
defineExpose({
selectedTab,
setSelectedComponent,
});
defineComponent({
ponents: {
TheHeader,
},
});
</script>
In your main
import { createApp } from 'vue';
import App from './App.vue';
import ActiveGoals from './ponents/ActiveGoals.vue';
import ManageGoals from './ponents/ManageGoals.vue';
const app = createApp(App);
app.ponent('active-goals', ActiveGoals)
app.ponent('manage-goals', ManageGoals)
app.mount('#app');
2: your .vue
<template>
<div>
<the-header></the-header>
<button @click="setSelectedComponent('active-goals')">Active goals</button>
<button @click="setSelectedComponent('manage-goals')">Manage goals</button>
<ponent :is="selectedTab"></ponent>
</div>
</template>
<script setup>
/* eslint-disable no-unused-vars */
import { ref, defineExpose, defineComponent } from 'vue';
import TheHeader from './ponents/TheHeader.vue';
import { defineAsyncComponent } from 'vue'
const ActiveGoals = defineAsyncComponent(() =>
import('./ponents/ActiveGoals.vue')
)
const ManageGoals = defineAsyncComponent(() =>
import('./ponents/ManageGoals.vue')
)
const selectedTab = ref(ActiveGoals);
const setSelectedComponent = (tab) => {
selectedTab.value = tab;
};
defineExpose({
selectedTab,
setSelectedComponent,
});
defineComponent({
ponents: {
TheHeader,
},
});
</script>
UPDATE 2
Playground with the tao's solution
UPDATE
Answering the question from tao:
To make <ponent :is="">
work with strings, you have to register you ponents with names.
You don't need defineComponent
in this case.
Check the original Vue Solution for Tabs in the Dynamic Components
Working Playground with your code
Here is you fixed code:
<script setup>
/* eslint-disable no-unused-vars */
import { ref, defineExpose, defineComponent } from 'vue';
import TheHeader from './TheHeader.vue';
import ActiveGoals from './ActiveGoals.vue';
import ManageGoals from './ManageGoals.vue';
const selectedTab = ref('active-goals');
const setSelectedComponent = (tab) => {
selectedTab.value = tab;
};
const tabs = {
'active-goals': ActiveGoals,
'manage-goals': ManageGoals
}
</script>
<template>
<the-header></the-header>
selectedTab: {{selectedTab}}<br/>
<button @click="setSelectedComponent('active-goals')">Active goals</button>
<button @click="setSelectedComponent('manage-goals')">Manage goals</button>
<hr/>
<ponent :is="tabs[selectedTab]"></ponent>
</template>