The Problem
Let's say we have a page template written as a Web component in a shared library to keep the company design system consistent. That page has some slots:
export class PageTemplate extends LitElement {
static properties = {
title: { type: String },
};
render() {
return html`
<div>
<h1>${title}</h1>
<slot name="template-body"></slot>
<div class="some-special-styles">
<slot name="template-buttons"></slot>
</div>
</div>
`;
}
}
customElements.define("page-template", PageTemplate);
Then we use this template in a Vue (v3.2.45) application on a base component to be used in the same app by multiple pages.
//page-base.vue
<template>
<page-template title="My App Name">
<slot name="base-body"></slot>
<slot name="base-buttons"></slot>
</page-template>
</template>
Here, we will use the page base vue component on a specific page.
//login-page.vue
<template>
<PageBase>
<template #base-body>
<div slot="template-body">
<input placeholder="some special code"/>
</div>
</template>
<template #base-buttons>
<button slot="template-buttons">login</button>
<button slot="template-buttons">back</button>
</template>
</PageBase>
</template>
To make the login page components show inside that original page template web component; we need to declare the slot property on the leaf components like in <button slot="template-buttons">
How can I implement the Vue Page Base component to avoid the need to remember to set the slot property in every leaf vue component?
Things I've Tryied
I've tried to solve this using the vanilla web syntax below, but Vue appears not to dispatch that information to the final HTML:
//page-base.vue
<template>
<page-template title="My App Name">
<!-- this does not work -->
<slot name="base-body" slot="template-body"></slot>
<slot name="base-buttons" slot="template-buttons"></slot>
</page-template>
</template>
There was also an attempt (after a suggestion in the comments) to use a template as a ghost intermediate in the page base. But nothing was rendered at runtime.
//page-base.vue
<template>
<page-template title="My App Name">
<!-- i can't have that span because of some-special-styles applied in the template-->
<template slot="template-body"><slot name="base-body"></slot></template>
<template slot="template-buttons"><slot name="base-buttons"></slot></template>
</page-template>
</template>
The approach to using some middle element to make the connection (like below) enables content rendering. Still, it does not work for the project requirements because, for style reasons, I need that the final components be the top-most nodes in the page template slots.
//page-base.vue
<template>
<page-template title="My App Name">
<!-- although it runs, i can't have these spans because of some-special-styles applied in the template -->
<span slot="template-body"><slot name="base-body"></slot></span>
<span slot="template-buttons"><slot name="base-buttons"></slot></span>
</page-template>
</template>