A slot is a placeholder or container that allows you to insert content into a designated area of a template or component. Slots provide a way to define a portion of a template/component that can be filled with custom content from the parent component or template. Owl is a component system built on templates. The owl must therefore be able to create generic components.
Slots provide a way to pass content from a parent component to a child component, allowing for greater flexibility and reusability. When you define a component, you can include slots within its template. Slots act as placeholders for content that will be provided by the parent component when the component is used. Here's a basic example:
Parent Component
<div>
<ChildComponent>
<p>This content will be placed in the default slot</p>
</ChildComponent>
</div>
Child Component
<div class="content">
<t t-slot="default"/>
</div>
When the parent component is rendered, the content provided within the child component tags is placed in the corresponding slots. It doesn't have to be text; you can pass any kind of template from the parent component. It could be a picture, a Font Awesome icon, or even another element.
Named Slot
Although default slots are very helpful, we might occasionally require more than one slot. So, in addition to the default slot, Owl supports named slots. Named slots allow you to define multiple slots within a component and target specific slots when providing content from the parent component. To use named slots, you specify a name attribute on the <slot> element in the child component's template and then use the slot attribute when passing content from the parent component.
Parent Component
<div>
<t t-set-slot="header_slot"/>
<p>Parent Template Content</p>
<t t-set-slot="footer_slot">
<p>Fallback Footer Content</p>
</t>
</div>
Child Component
<div>
<t t-call="parent_template">
<t t-slot="header_slot">
<h1>Child Template Header</h1>
</t>
<t t-slot="footer_slot">
<p>Child Template Footer</p>
</t>
</t>
</div>
In this example, the parent_template defines two slots: header_slot and footer_slot. The content specified in the child_template will replace the corresponding slots in the parent_template when the view is rendered.
Default content
If the parent did not define them, slots can define a default content:
<div t-name="Parent">
<Child/>
</div>
<span t-name="Child">
<t t-slot="default">default content</t>
</span>
Dynamic Slots
In fact, string interpolation allows the t-slot directive to use any expression:
<t t-slot="{{current}}" />
This will evaluate the current expression and replace the t-slot directive with the slot that corresponds to it.
Slots and props
Slots define some data to pass to the child component, making them somewhat similar to props in this regard. Owl actually defines a special prop slot that contains all slot information provided to the component in order to make it possible to use it and pass it down to sub-components. It appears as follows:
{ slotName_1: slotInfo_1, ..., slotName_m: slotInfo_m }
So, a component can pass its slots to a subcomponent like this:
<Child slots="props.slots"/>
Slot Params
It could be essential to give more data to a slot for more complex use cases. To achieve this, add more key/value pairs to the t-set-slot directive. The generic component will then be able to read them in its prop slots.
For example
class Notebook extends Component {
static template = xml`
<div class="notebook">
<div class="tabs">
<t t-foreach="tabNames" t-as="tab" t-key="tab_index">
<span t-att-class="{active:tab_index === activeTab}" t-on-click="() => state.activeTab=tab_index">
<t t-esc="props.slots[tab].title"/>
</span>
</t>
</div>
<div class="page">
<t t-slot="{{currentSlot}}"/>
</div>
</div>`;
setup() {
this.state = useState({ activeTab: 0 });
this.tabNames = Object.keys(this.props.slots);
}
get currentSlot() {
return this.tabNames[this.state.activeTab];
}
}
Take note of how the title value for each slot can be read. Here are some examples of uses for this Notebook component:
<Notebook>
<t t-set-slot="page1" title="'Page 1'">
<div>this is in the page 1</div>
</t>
<t t-set-slot="page2" title="'Page 2'" hidden="somevalue">
<div>this is in the page 2</div>
</t>
</Notebook>
If necessary, one can bind a function using the .bind suffix because slot params function like regular props.
Slot Scopes
The content of a slot may depend on some information related to the generic component for various types of advanced use cases. The slot parameters are the opposite of this.
The t-slot-scope directive can be used in conjunction with the t-set-slot to resolve issues of this nature. This establishes the name of a variable with access to all information provided by the child component:
<MyComponent>
<t t-set-slot="foo" t-slot-scope="scope">
content
<t t-esc="scope.bool"/>
<t t-esc="scope.num"/>
</t>
</MyComponent>
The child component that has the slot also has the ability to deliver values in the following formats:
<t t-slot="foo" bool="other_var" num="5">
or this
<t t-slot="foo" t-props="someObject">
If the default slot is involved, you can declare the slot scope right on the component:
<MyComponent t-slot-scope="scope">
content
<t t-esc="scope.bool"/>
<t t-esc="scope.num"/>
</MyComponent>
Overall, by separating a component's structure from its content, slots in Owl let you build reusable and adaptable components. They facilitate communication between the parent and child components, and they also offer a mechanism for dynamic content injection. You may create more composable and modular templates with slots.