Ranuras (Slots)
Owl es un sistema de componentes basado en plantillas. Por lo tanto, existe la necesidad de poder crear componentes genéricos. Por ejemplo, imagine un componente Navbar
genérico, que muestra una barra de navegación, pero con algún contenido personalizable. Dado que el contenido específico solo lo conoce el usuario de la Navbar
, sería bueno especificarlo en la plantilla donde se utiliza Navbar
:
<div> <Navbar> <span>Hello Owl</span> </Navbar> </div>
¡Así es exactamente como funcionan los slots! En el ejemplo anterior, el usuario del componente Navbar
especifica algún contenido (aquí, en el slot predeterminado). El componente Navbar
puede insertar ese contenido en su propia plantilla en la ubicación adecuada. Una información importante a tener en cuenta es que el contenido del slot se representa en el contexto principal, no en la barra de navegación. Como tal, puede acceder a valores y métodos desde el componente principal.
Así es como se podría definir el componente Navbar
, con la directiva t-slot
:
<div class="navbar"> <t t-slot="default"/> <ul> <!-- rest of the navbar here --> </ul></div>
Named slots
Las ranuras predeterminadas son muy útiles, pero a veces, podemos necesitar más de una ranura. ¡Para eso están las ranuras con nombre! Por ejemplo, supongamos que implementamos un componente InfoBox
que muestra un título y algún contenido específico. Su plantilla podría verse así:
<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> </div> <div class="info-box-content"> <t t-slot="content"/> </div></div>
Y se podría utilizar con la directiva t-set-slot
:
<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>
Renderizando el contexto
El contenido de las ranuras se representa en realidad con el contexto de representación correspondiente a donde se definió, no donde está ubicado. Esto permite al usuario definir controladores de eventos que se vincularán al componente correcto (normalmente, el componente principal del contenido de la ranura).
Ranuras por defecto
Todos los elementos dentro del componente que no sean una ranura con nombre se tratarán como parte del contenido de la ranura “predeterminada”. Por ejemplo:
<div t-name="Parent"> <Child> <span>some content</span> </Child></div>
<div t-name="Child"> <t t-slot="default"/></div>
Se pueden mezclar ranuras predeterminadas y ranuras con nombre:
<div> <Child> default content <t t-set-slot="footer"> content for footer slot here </t> </Child></div>
Contenido por defecto
Las ranuras pueden definir un contenido predeterminado, en caso de que el padre no las haya definido:
<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> -->
Ranuras dinámicas
La directiva t-slot
realmente puede usar cualquier expresión, utilizando la interpolación de cadenas:
<t t-slot="{{current}}" />
Esto evaluará la expresión actual
e insertará la ranura correspondiente en el lugar de la directiva t-slot
.
Ranuras y Props
En cierto sentido, las ranuras son casi lo mismo que una propiedad: definen cierta información para pasar al componente secundario. Para que sea posible usarla y pasarla al subcomponente, Owl define una propiedad especial slots
que contiene toda la información de las ranuras que se le da al componente. Se ve así:
{ slotName_1: slotInfo_1, ..., slotName_m: slotInfo_m }
Entonces, un componente puede pasar sus ranuras a un subcomponente de la siguiente manera:
<Child slots="props.slots"/>
Parámetros en las ranuras
Para casos de uso avanzados, puede ser necesario pasar información adicional a una ranura. Esto se puede hacer proporcionando pares clave/valor adicionales a la directiva t-set-slot
. Luego, el componente genérico puede leerlos en su propiedad slots
.
Por ejemplo, aquí se muestra cómo se podría implementar un componente Notebook (un componente con varias páginas y una barra de pestañas, que solo representa la página activa actual y cada página tiene un título).
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]; }}
Observe cómo se puede leer el valor del título
para cada ranura. Así es como se puede utilizar este componente Notebook
:
<Notebook> <t t-set-slot="page1" title.translate="Page 1"> <div>this is in the page 1</div> </t> <t t-set-slot="page2" title.translate="Page 2" hidden="somevalue"> <div>this is in the page 2</div> </t></Notebook>
Los parámetros de ranura funcionan como propiedades normales, por lo que se pueden usar sufijos como .translate
cuando una propiedad es una cadena que ve el usuario y debe traducirse, o .bind
para vincular una función si es necesario.
Alcance de las ranuras
Para otros tipos de casos de uso avanzados, el contenido de una ranura puede depender de cierta información específica del componente genérico. Esto es lo opuesto a los parámetros de la ranura.
Para resolver este tipo de problemas, se puede utilizar la directiva t-slot-scope
junto con t-set-slot
. Esto define el nombre de una variable que puede acceder a todo lo que proporciona el componente secundario:
<MyComponent> <t t-set-slot="foo" t-slot-scope="scope"> content <t t-esc="scope.bool"/> <t t-esc="scope.num"/> </t></MyComponent>
Y el componente secundario que incluye la ranura puede proporcionar valores como este:
<t t-slot="foo" bool="other_var" num="5">
o esto:
<t t-slot="foo" t-props="someObject">
En el caso de la ranura predeterminada, puede declarar el alcance de la ranura directamente en el componente mismo:
<MyComponent t-slot-scope="scope"> content <t t-esc="scope.bool"/> <t t-esc="scope.num"/></MyComponent>
Los valores de ranura funcionan como accesorios normales, por lo que se puede usar el sufijo .bind
para vincular una función si es necesario.