An embed slot provides a place in a template where other content can be loaded.
Embed slots are commonly used for:
Embed slots are declared with the special type name "embed-slot
":
{
type: "div", A <div> element...
$: {
type: "embed-slot", ...with a contained embed-slot
}
}
To set the content of an embed slot, use the content
property.
This can be done with a dynamic callback:
class Main extends Component
{
getContent()
{
return "Hello World";
}
static template = {
type: "DIV",
$: {
type: "embed-slot",
content: c => c.getContent(),
}
}
}
The content of an embed-slot can also be set programatically by binding
the embed slot and directly setting its content
property.
class Main extends Component
{
constructor()
{
super();
// Create DOM
this.create();
// Set content
this.slot.content = "Hello World";
}
static template = {
type: "DIV",
$: {
type: "embed-slot",
bind: "slot"
}
}
}
Embed slots support various kinds of content.
Setting a plain string as the content
property will create a text node as
the content.
It is safe to set untrusted data with this approach.
{
type: "embed-slot",
content: () => "<Hello World>",
}
To set the content to a HTML string, use the html()
directive.
It is not safe to set untrusted data with this approach.
{
type: "embed-slot",
content: () => html("<em>Hello World</em>"),
}
The content property can be set to a component instance
class MyComponent extends Component
{
static template = {
type: "div",
text: "This is a component"
}
}
{
type: "embed-slot",
content: () => new MyComponent(),
}
An embed-slot's content can be set to either an array of, or a single HTML DOM node.
{
type: "embed-slot",
content: () => [
document.createElement("hr"),
document.createTextNode("Hello World"),
document.createElement("hr"),
]
}
An embed slot can have a placeholder that's shown when the content property is not set:
{
type: "embed-slot",
content: c => c.content,
placeholder: html("<p>Nothing to see here</p>"),
},
A component can export embed slots letting content from an outer containing template to be injected (or "pushed down") into the component.
Consider the following component which implements a custom link that has some special click handling logic:
class MyLink extends Component
{
#href = "#";
get href() { return this.#href; }
set href(value) { this.#href = value; this.invalidate() }
#title = "link";
get title() { return this.#title; }
set title(value) { this.#title = value; this.invalidate() }
on_click(ev)
{
ev.preventDefault();
alert("Special link handling done here!");
}
static template = {
type: "a",
href: c => c.href,
on_click: (c, ev) => c.on_click(ev),
$: c => " " + c.title + " "
};
}
class Main extends Component
{
static template = {
type: "div",
$: [
{ type: MyLink, href: "/", title: "Home" },
{ type: MyLink, href: "/profile", title: "Profile" },
]
}
}
The MyLink
class provides a nice way to encapsulate the special link handling
logic however it's currently limited to only being able to show plain text as
the link text.
We can improve on this by using an embed-slot to allow any arbitrary content to be used as the link content:
class MyLink extends Component
{
static template = {
type: "a",
href: c => c.href,
on_click: (c, ev) => c.on_click(ev),
$: {
type: "embed-slot", Special name "embed-slot"
bind: "content", Make this slot available as a component property
placeholder: c => " " + c.title + " ", Revert to text if no slot content
},
};
static slots = [ "content" ]; Slot names need to be declared
}
[
{
type: MyLink,
href: "/",
content: {
type: "img",
src: "/public/codeonly-icon.svg",
style: "margin-right: 10px",
width: 24,
height: 24,
}
},
{ type: MyLink, href: "/profile", title: "Profile" },
]
Note the following:
Instead of setting the text title
property, we can now provide
templated DOM elements via the content
property
If we don't set the content property, the title
property still
works thanks to the placeholder
property on the EmbedSlot
.
The embedded content could have dynamic properties referencing the outer component class instance and they would update as per any other template content.
A component that has embed slots must declare them using the static
slots
property on the component class. The template compiler
needs to generate special code for embed slots and needs to known
up front which properties are templates.
Since $
is an alias for a component's content
property, we
could have used $: {...}
instead of content: {...}
.
Embed slots can be used in conjunction with CodeOnly CSS transitions to produce animation effects when the content changes.
See Transitions for more on this.