Reputation: 2712
Using Vue 3.2.41 - @heroicons/vue 2.0.14 - inertiajs 1.0 - vite 4.0.0
I'm calling a Vue component using this:
<TimelineItem icon="CalendarDaysIcon" />
The component looks like this:
<template>
<component :is="icon" /> <!-- doesn't work -->
<CalendarDaysIcon /> <!-- works -->
</template>
<script setup>
import {
CalendarDaysIcon,
} from '@heroicons/vue/20/solid'
const props = defineProps(['icon'])
</script>
The HTML being rendered is like this:
<calendardaysicon></calendardaysicon> <!-- not what I want -->
<svg> ... </svg> <!-- correct but not dynamic -->
In other words, the <component :is />
is just rendering some empty <calendardaysicon>
tags when I'd expect it to render the component. I can see that it has made it lowercase and have no idea how to force it back to PascalCase and I'm not even sure if that would help the situation.
I've simplified the code somewhat, but the full version would have a list of 10 different icons (all part of the Heroicons package which uses PascalCase names) which I'd like to be able to call easily from the main component.
Upvotes: 2
Views: 447
Reputation: 2712
Using <component :is="icon" />
is only using a string containing CalendarDaysIcon
Instead, in the main component, pass the actual component reference like this:
<template>
<TimelineItem :icon="CalendarDaysIcon" />
</template>
<script setup>
import {
CalendarDaysIcon,
} from '@heroicons/vue/20/solid'
const props = defineProps(['icon'])
</script>
Then, in the TimelineItem
component, there is no need to reference any icons:
<template>
<component :is="icon" /> <!-- now works -->
</template>
<script setup>
const props = defineProps(['icon'])
</script>
Thanks to @Robert Boes
on the Inertia Discord server for the guidance.
Upvotes: 0
Reputation: 5183
const { createApp, createElementVNode, openBlock, createElementBlock } = Vue;
const CalendarDaysIcon = {
render(_ctx, _cache) {
return (openBlock(), createElementBlock("svg", {
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 20 20",
fill: "currentColor",
"aria-hidden": "true"
}, [
createElementVNode("path", { d: "M5.25 12a.75.75 0 01.75-.75h.01a.75.75 0 01.75.75v.01a.75.75 0 01-.75.75H6a.75.75 0 01-.75-.75V12zM6 13.25a.75.75 0 00-.75.75v.01c0 .414.336.75.75.75h.01a.75.75 0 00.75-.75V14a.75.75 0 00-.75-.75H6zM7.25 12a.75.75 0 01.75-.75h.01a.75.75 0 01.75.75v.01a.75.75 0 01-.75.75H8a.75.75 0 01-.75-.75V12zM8 13.25a.75.75 0 00-.75.75v.01c0 .414.336.75.75.75h.01a.75.75 0 00.75-.75V14a.75.75 0 00-.75-.75H8zM9.25 10a.75.75 0 01.75-.75h.01a.75.75 0 01.75.75v.01a.75.75 0 01-.75.75H10a.75.75 0 01-.75-.75V10zM10 11.25a.75.75 0 00-.75.75v.01c0 .414.336.75.75.75h.01a.75.75 0 00.75-.75V12a.75.75 0 00-.75-.75H10zM9.25 14a.75.75 0 01.75-.75h.01a.75.75 0 01.75.75v.01a.75.75 0 01-.75.75H10a.75.75 0 01-.75-.75V14zM12 9.25a.75.75 0 00-.75.75v.01c0 .414.336.75.75.75h.01a.75.75 0 00.75-.75V10a.75.75 0 00-.75-.75H12zM11.25 12a.75.75 0 01.75-.75h.01a.75.75 0 01.75.75v.01a.75.75 0 01-.75.75H12a.75.75 0 01-.75-.75V12zM12 13.25a.75.75 0 00-.75.75v.01c0 .414.336.75.75.75h.01a.75.75 0 00.75-.75V14a.75.75 0 00-.75-.75H12zM13.25 10a.75.75 0 01.75-.75h.01a.75.75 0 01.75.75v.01a.75.75 0 01-.75.75H14a.75.75 0 01-.75-.75V10zM14 11.25a.75.75 0 00-.75.75v.01c0 .414.336.75.75.75h.01a.75.75 0 00.75-.75V12a.75.75 0 00-.75-.75H14z" }),
createElementVNode("path", {
"fill-rule": "evenodd",
d: "M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z",
"clip-rule": "evenodd"
})
]))
}
}
const TimeLineItem = {
props: ['icon'],
components: {
CalendarDaysIcon
},
template: '<component :is="icon" class="icon"/>'
}
const App = {
components: {
TimeLineItem
}
}
const app = createApp(App)
app.mount('#app')
.icon {
width: 36px;
height: 36px;
}
<div id="app">
<time-line-item icon="CalendarDaysIcon"></time-line-item>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
Upvotes: 0
Reputation: 296
If you are authoring your templates directly in a DOM (e.g. as the content of a native element), the template will be subject to the browser's native HTML parsing behavior. In such cases, you will need to use kebab-case and explicit closing tags for components https://vuejs.org/guide/essentials/component-basics.html#dom-template-parsing-caveats
Try: <calendar-days-icon> </calendar-days-icon>
Upvotes: 2