In my vue 3 script setup component, I have this
<template>
<table>
<tbody>
<template v-for="(row, i) in componentProps.rows">
<tr v-for="(header, j) in componentProps.headers" :key="i + '-' + j" :data-alternating="i%2===0 ? 'even' : 'odd'">
<td class="font-weight-bold text-caption">
{{ header.title }}
</td>
<td>
<template #test>
</template>
</td>
</tr>
</template>
</tbody>
</table>
</template>
<script setup lang='ts'>
const componentProps = defineProps<{
headers: TableHeader[];
rows: {[name:string]:any}[];
}>();
</script>
But I get this error
And vite complains this too
Codegen node is missing for element/if/for node. Apply appropriate transforms first.
4:00:10 PM [vite] Internal server error: Codegen node is missing for element/if/for node. Apply appropriate transforms first.
Plugin: vite:vue
How can I resolve this?
Slots are a way for parent components to inject content inside child components, rendered at predetermined locations inside the child component's template.
In more detail: you declare a <slot name="test" />
1 inside a (child) component's <template>
. You can put fallback content inside the slot. This fallback content will be rendered if the parent component doesn't specify anything for the slot.
Example ComponentA.vue:
<template>
<slot name="test">
Fallback content
</slot>
More child component content
</template>
Then you import this component in another component and you use it as 2:
<template>
Some parent content...
<component-a>
<template #test>
I am injected content
</template>
</component-a>
More parent content...
</template>
which will render:
Some parent content...
I am injected content
More child component content
More parent content...
Without <template #test>
specified (e.g: <component-a />
) it would render:
Some parent content...
Fallback content
More child component content
More parent content...
So, before you can use <template #test>
anywhere, you need to declare <slot name="test" />
inside a component and then import that component into any other component, and use <template #test>
to inject parent defined content into the imported component's template, to replace the fallback content of the slot (if it has any).
And what makes slots really interesting and particularly useful is their ability to expose a scope to the parent component's template, which the parent component can use to output dynamic content, by combining the slot scope's logic with their own logic.
Read more on that here.
1 - slots can also be unnamed. Default (or unnamed) slots (<slot />
) are actually a special case of named slots: under the hood, they are named default
, and also, implicitly, they get all the content you put inside the child component's tag, in the parent component. For example, if the above ComponentA.vue's template looked like this:
<template>
<slot />
Bla bla...
</template>
when used as
<component-a>
Whatever
</component-a>
...would render:
Whatever
Bla bla...
which would also be the result of the full blown syntax:
<component-a>
<template #default>
Whatever
</template>
</component-a>
2 - Slots work with both DOM text nodes and HTML elements, like normal components. I opted not to use HTML elements here to keep things simple. If you used the exact examples as above you'd get all the content rendered in one single text line, since all text is inline content in HTML.