2020-02-15 10:08:49 +01:00
|
|
|
# 🦉 Slots 🦉
|
|
|
|
|
|
|
|
|
|
## Content
|
|
|
|
|
|
|
|
|
|
- [Overview](#overview)
|
2022-01-18 14:35:05 +01:00
|
|
|
- [Named slots](#named-slots)
|
|
|
|
|
- [Rendering Context](#rendering-context)
|
|
|
|
|
- [Default Slot](#default-slot)
|
|
|
|
|
- [Default Content](#default-content)
|
|
|
|
|
- [Dynamic slots](#dynamic-slots)
|
|
|
|
|
- [Slots and props](#slots-and-props)
|
|
|
|
|
- [Slot params](#slot-params)
|
|
|
|
|
- [Slot scopes](#slot-scopes)
|
2020-02-15 10:08:49 +01:00
|
|
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
|
|
|
|
Owl is a template based component system. There is therefore a need to be able
|
2022-01-18 14:35:05 +01:00
|
|
|
to make generic components. For example, imagine a generic `Navbar`
|
|
|
|
|
component, which displays a navbar, but with some customizable content. Since
|
|
|
|
|
the specific content is only known to the user of the `Navbar`, it would be nice
|
|
|
|
|
to specify it in the template where `Navbar` is used:
|
2020-02-15 10:08:49 +01:00
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
```xml
|
|
|
|
|
<div>
|
|
|
|
|
<Navbar>
|
|
|
|
|
<span>Hello Owl</span>
|
|
|
|
|
</Navbar>
|
|
|
|
|
</div>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is exactly the way slots work! In the example above, the user of the `Navbar`
|
|
|
|
|
component specify some content (here, in the default slot). The `Navbar`
|
|
|
|
|
component can insert that content in its own template at the appropriate location.
|
|
|
|
|
An important information to notice is that the content of the slot is rendered in
|
|
|
|
|
the parent context, not in the navbar. As such, it can access values and methods
|
|
|
|
|
from the parent component.
|
2020-02-15 10:08:49 +01:00
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
Here is how the `Navbar` component could be defined, with the `t-slot` directive:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<div class="navbar">
|
|
|
|
|
<t t-slot="default"/>
|
|
|
|
|
<ul>
|
|
|
|
|
<!-- rest of the navbar here -->
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
```
|
2020-02-15 10:08:49 +01:00
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
## Named slots
|
2020-02-15 10:08:49 +01:00
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
Default slots are very useful, but sometimes, we may need more than one slot.
|
|
|
|
|
This is what named slots are for! For example, suppose we implement a component
|
|
|
|
|
`InfoBox` that display a title and some specific content. Its template could look
|
|
|
|
|
like this:
|
2020-10-30 12:12:08 +01:00
|
|
|
|
2020-02-15 10:08:49 +01:00
|
|
|
```xml
|
2022-01-18 14:35:05 +01:00
|
|
|
<div class="info-box">
|
|
|
|
|
<div class="info-box-title">
|
|
|
|
|
<t t-slot="title"/>
|
|
|
|
|
<span class="info-box-close-button" t-on-click="close">X</span>
|
2020-02-15 10:08:49 +01:00
|
|
|
</div>
|
2022-01-18 14:35:05 +01:00
|
|
|
<div class="info-box-content">
|
|
|
|
|
<t t-slot="content"/>
|
2020-02-15 10:08:49 +01:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
```
|
|
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
And one could use it with the `t-set-slot` directive:
|
2020-02-15 10:08:49 +01:00
|
|
|
|
|
|
|
|
```xml
|
2022-01-18 14:35:05 +01:00
|
|
|
<InfoBox>
|
|
|
|
|
<t t-set-slot="title">
|
|
|
|
|
Specific Title. It could be html also.
|
|
|
|
|
</t>
|
|
|
|
|
<t t-set-slot="content">
|
|
|
|
|
<!-- some template here, with html, events, whatever -->
|
|
|
|
|
</t>
|
|
|
|
|
</InfoBox>
|
2020-02-15 10:08:49 +01:00
|
|
|
```
|
|
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
## Rendering context
|
2020-04-06 15:29:54 +02:00
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
The content of the slots is actually rendered with the rendering context corresponding
|
|
|
|
|
to where it was defined, not where it is positioned. This allows the user to define
|
|
|
|
|
event handlers that will be bound to the correct component (usually, the
|
|
|
|
|
grandparent of the slot content).
|
2020-02-15 10:08:49 +01:00
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
## Default Slot
|
2020-10-30 12:12:08 +01:00
|
|
|
|
|
|
|
|
The first element inside the component which is not a named slot will
|
2020-02-15 10:08:49 +01:00
|
|
|
be considered the `default` slot. For example:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<div t-name="Parent">
|
|
|
|
|
<Child>
|
|
|
|
|
<span>some content</span>
|
|
|
|
|
</Child>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div t-name="Child">
|
|
|
|
|
<t t-slot="default"/>
|
|
|
|
|
</div>
|
|
|
|
|
```
|
|
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
One can mix default slot and named slots:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<div>
|
|
|
|
|
<Child>
|
|
|
|
|
default content
|
|
|
|
|
<t t-set-slot="footer">
|
|
|
|
|
content for footer slot here
|
|
|
|
|
</Child>
|
|
|
|
|
</div>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Default content
|
2020-10-30 12:12:08 +01:00
|
|
|
|
|
|
|
|
Slots can define a default content, in case the parent did not define them:
|
2020-02-15 10:08:49 +01:00
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<div t-name="Parent">
|
|
|
|
|
<Child/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<span t-name="Child">
|
|
|
|
|
<t t-slot="default">default content</t>
|
|
|
|
|
</span>
|
|
|
|
|
<!-- will be rendered as: <div><span>default content</span></div> -->
|
|
|
|
|
```
|
|
|
|
|
|
2022-01-18 14:35:05 +01:00
|
|
|
## Dynamic Slots
|
2020-10-30 12:12:08 +01:00
|
|
|
|
|
|
|
|
The `t-slot` directive is actually able to use any expressions, using string
|
|
|
|
|
interplolation:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<t t-slot="{{current}}" />
|
|
|
|
|
```
|
2022-01-18 14:35:05 +01:00
|
|
|
|
|
|
|
|
This will evaluate the `current` expression, and insert the corresponding slot
|
|
|
|
|
at the place of the `t-slot` directive.
|
|
|
|
|
|
|
|
|
|
## Slots and props
|
|
|
|
|
|
|
|
|
|
In a sense, slots are almost the same as a prop: they define some information
|
|
|
|
|
to pass to the child component. To make it possible to use it, and to pass it
|
|
|
|
|
down to sub component, Owl actually define a special prop `slots` that contains
|
|
|
|
|
all slot information given to the component. It looks like this:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
{ slotName_1: slotInfo_1, ..., slotName_m: slotInfo_m }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
So, a component can pass its slots to a subcomponent like this:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<Child slots="props.slots"/>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Slot params
|
|
|
|
|
|
|
|
|
|
For advanced usecases, it may be necessary to pass additional information to a
|
|
|
|
|
slot. This can be done by providing extra key/value pairs to the `t-set-slot`
|
|
|
|
|
directive. Then, the generic component can read them in its prop `slots`.
|
|
|
|
|
|
|
|
|
|
For example, here is how a Notebook component could be implemented (a component
|
|
|
|
|
with multiple page, and a tab bar, which only render the current active page,
|
|
|
|
|
and each page has a title).
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
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">
|
|
|
|
|
<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];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Notice how one can read the `title` value for each slots. Here is how one could
|
|
|
|
|
use this `Notebook` component:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<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>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Slot scopes
|
|
|
|
|
|
|
|
|
|
For other kind of advanced use cases, the content of a slot may depends on some
|
|
|
|
|
specific information specific to the generic component. This is the opposite
|
|
|
|
|
of the slot params.
|
|
|
|
|
|
2022-01-24 16:35:22 +01:00
|
|
|
To solve this kind of problems, one can use the `t-slot-scope` directive along
|
2022-01-18 14:35:05 +01:00
|
|
|
with the `t-set-slot`. This defines the name of a variable that can access
|
|
|
|
|
everything given by the child component:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<div>
|
2022-01-24 16:35:22 +01:00
|
|
|
<t t-set-slot="foo" t-slot-scope="scope">
|
2022-01-18 14:35:05 +01:00
|
|
|
content
|
|
|
|
|
<t t-esc="scope.bool"/>
|
|
|
|
|
<t t-esc="scope.num"/>
|
|
|
|
|
</t>
|
|
|
|
|
</div>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
And the child component that includes the slot can provide values like this:
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<div>
|
|
|
|
|
<t t-slot="foo" bool="other_var" num="5">
|
|
|
|
|
</div>
|
|
|
|
|
```
|