Plantillas
Las plantillas Owl se describen utilizando la especificación QWeb. Se basa en el formato XML y se utiliza principalmente para generar HTML. En OWL, las plantillas QWeb se compilan en funciones que generan una representación DOM virtual del HTML. Además, dado que Owl es un sistema de componentes en vivo, existen directivas adicionales especÃficas de Owl (como t-on
).
<div> <span t-if="somecondition">Some string</span> <ul t-else=""> <li t-foreach="messages" t-as="message"> <t t-esc="message"/> </li> </ul></div>
Las directivas de plantilla se especifican como atributos XML con el prefijo t-
, por ejemplo t-if
para condicionales, donde los elementos y otros atributos se representan directamente.
Para evitar la representación de elementos, también está disponible un elemento marcador <t>
, que ejecuta su directiva pero no genera ninguna salida por sà mismo.
Presentamos en esta sección el lenguaje de plantillas, incluidas sus extensiones especÃficas de Owl.
Directivas
Como referencia, aquà hay una lista de todas las directivas estándar de QWeb:
Nombre | Descripción |
---|---|
t-esc | Generar un valor de forma segura |
t-out | Salida de valor, posiblemente sin escape |
t-set , t-value | Configuración de variables |
t-if , t-elif , t-else , | condicionales |
t-foreach , t-as | Bucles |
t-att , t-attf-* , t-att-* | Atributos dinámicos |
t-call | Renderizado de subplantillas |
t-debug , t-log | Depuración |
t-translation | Deshabilitar la traducción de un nodo |
The component system in Owl requires additional directives, to express various needs. Here is a list of all Owl specific directives:
Nombre | Descripción |
---|---|
t-component , t-props | Definición de un subcomponente |
t-ref | Establecer una referencia a un nodo DOM o un subcomponente |
t-key | Definición de una clave (para facilitar la conciliación del DOM virtual) |
t-on-* | Manejo de eventos |
t-portal | Portal |
t-slot , t-set-slot , t-slot-scope | Renderizado de ranuras |
t-model | Enlaces de entrada de formulario |
t-tag | Representación de nodos con nombre de etiqueta dinámico |
t-custom-* | Representación de nodos con directivas personalizadas |
Referencia de plantilla de QWeb
Espacios en blanco
Los espacios en blanco en una plantilla se manejan de una manera especial:
- Los espacios en blanco consecutivos siempre se condensan en un solo espacio en blanco.
- Si un nodo de texto que solo contiene espacios en blanco contiene un salto de lÃnea, se ignora.
- Las reglas anteriores no se aplican si estamos en una etiqueta
<pre>
Evaluación de expresión
Las expresiones de QWeb son cadenas que se procesarán en el momento de la compilación. Cada variable de la expresión de JavaScript se reemplazará con una búsqueda en el contexto (es decir, el componente). Por ejemplo, a + b.c(d)
se convertirá en:
context["a"] + context["b"].c(context["d"]);
Es útil explicar las distintas reglas que se aplican a estas expresiones:
-
Debe ser una expresión simple que devuelva un valor. No puede ser una declaración.
<div><p t-if="1 + 2 === 3">ok</p></div>es válido, pero lo siguiente no es válido:
<div><p t-if="console.log(1)">NOT valid</p></div> -
Puede utilizar cualquier cosa en el contexto de representación (que normalmente contiene las propiedades del componente):
<p t-if="user.birthday === today()">Happy bithday!</p>es válido y leerá el objeto
user
del contexto y llamará a la funcióntoday
. -
Puede utilizar algunos operadores especiales para evitar el uso de sÃmbolos como
<
,>
,&
o|
. Esto resulta útil para asegurarnos de que seguimos escribiendo XML válido.Palabra reemplazado con and
&&
or
||
gt
>
gte
>=
lt
<
lte
<=
Entonces, uno puede escribir esto:
<div><p t-if="10 + 2 gt 5">ok</p></div>
Nodos HTML estáticos
Los nodos HTML normales y regulares se representan en sà mismos:
<div>hello</div> <!–– rendered as itself ––>
Salida de datos
La directiva t-esc
es necesaria siempre que se desee agregar una expresión de texto dinámica en una plantilla. El texto se escapa para evitar problemas de seguridad.
<p><t t-esc="value"/></p>
Representado con el valor value
establecido en 42
en el contexto de representación, da como resultado:
<p>42</p>
La directiva t-out
es casi la misma que t-esc
, pero posiblemente sin el escape. La diferencia es que el valor recibido por la directiva t-out
solo no será escapado si ha sido marcado como tal, utilizando la función de utilidad markup
:
Por ejemplo, en el siguiente componente:
const { markup, Component, xml } = owl;
class SomeComponent extends Component { static template = xml` <t t-out="value1"/> <t t-out="value2"/>`;
value1 = "<div>some text 1</div>"; value2 = markup("<div>some text 2</div>");}
El primer t-out
actuará como una directiva t-esc
, lo que significa que se escapará el contenido de value1
. Sin embargo, dado que value2
se ha etiquetado como un marcado, se inyectará como html.
Configuración de variables
QWeb permite crear variables desde dentro de la plantilla, para memorizar un cálculo (para usarlo varias veces), darle a un dato un nombre más claro, …
Esto se hace mediante la directiva t-set
, que toma el nombre de la variable a crear. El valor a establecer se puede proporcionar de dos maneras:
-
un atributo
t-value
que contiene una expresión, y se establecerá el resultado de su evaluación:<t t-set="foo" t-value="2 + 1"/><t t-esc="foo"/>imprimirá
3
. Tenga en cuenta que la evaluación se realiza en el momento de la representación, no en el momento de la compilación. -
Si no hay ningún atributo
t-value
, se guarda el cuerpo del nodo y su valor se establece como el valor de la variable:<t t-set="foo"><li>ok</li></t><t t-esc="foo"/>generará
<li>ok</li>
(el contenido se escapa ya que usamos la directivat-esc
)
La directiva t-set
actúa como una variable regular en la mayorÃa de los lenguajes de programación. Tiene un alcance léxico (los nodos internos son subámbitos), se puede ocultar…
Condicionales
La directiva t-if
es útil para representar algo de manera condicional. Evalúa la expresión dada como valor de atributo y luego actúa en consecuencia.
<div> <t t-if="condition"> <p>ok</p> </t></div>
El elemento se representa si la condición (evaluada con el contexto de representación actual) es verdadera:
<div> <p>ok</p></div>
pero si la condición es falsa se elimina del resultado:
<div></div>
La representación condicional se aplica al portador de la directiva, que no tiene por qué ser <t>
:
<div> <p t-if="condition">ok</p></div>
Dará los mismos resultados que el ejemplo anterior.
Las directivas de ramificación condicional adicionales t-elif
y t-else
también están disponibles:
<div> <p t-if="user.birthday == today()">Happy bithday!</p> <p t-elif="user.login == 'root'">Welcome master!</p> <p t-else="">Welcome!</p></div>
Atributos dinámicos
Se puede utilizar la directiva t-att-
para agregar atributos dinámicos. Su uso principal es evaluar una expresión (en el momento de la representación) y vincular un atributo a su resultado:
Por ejemplo, si tenemos id
establecido en 32 en el contexto de representación,
<div t-att-data-action-id="id"/> <!-- result: <div data-action-id="32"></div> -->
Si una expresión se evalúa como un valor falso, no se establecerá en absoluto:
<div t-att-foo="false"/> <!-- result: <div></div> -->
A veces resulta conveniente formatear un atributo con interpolación de cadenas. En ese caso, se puede utilizar la directiva t-attf-
. Es útil cuando necesitamos mezclar elementos literales y dinámicos, como las clases CSS. Los elementos dinámicos se pueden especificar con {{...}}
o #{...}
:
<div t-attf-foo="a {{value1}} is #{value2} of {{value3}} ]"/><!-- result if values are set to 1,2 and 3: <div foo="a 0 is 1 of 2 ]"></div> -->
Si necesitamos nombres de atributos completamente dinámicos, existe una directiva adicional: t-att
, que acepta un objeto (con claves asignadas a sus valores) o un par [clave, valor]
. Por ejemplo:
<div t-att="{'a': 1, 'b': 2}"/> <!-- result: <div a="1" b="2"></div> -->
<div t-att="['a', 'b']"/> <!-- <div a="b"></div> -->
Atributos de clase dinámicos
Para mayor comodidad, Owl admite un caso especial para el caso t-att-class
: se puede utilizar un objeto con claves que describen las clases y valores booleanos que indican si la clase está o no presente:
<div t-att-class="{'a': true, 'b': true}"/> <!-- result: <div class="a b"></div> -->
<div t-att-class="{'a b': true, 'c': true}"/> <!-- result: <div class="a b c"></div> -->
Tenga en cuenta que se puede combinar con el atributo de clase normal:
<div class="a" t-att-class="{'b': true}"/> <!-- result: <div class="a b"></div> -->
Nombre de etiquetas dinámicos
Al escribir componentes o plantillas genéricas, todavÃa no se conoce la etiqueta concreta especÃfica para un elemento HTML. En esas situaciones, la directiva t-tag
resulta útil. Simplemente evalúa dinámicamente una expresión para usarla como nombre de etiqueta. La plantilla:
<t t-tag="tag"> <span>content</span></t>
se representará como <div><span>content</span></div>
si la clave de contexto tag
se establece en div
.
Bucles
QWeb tiene una directiva de iteración t-foreach
que toma una expresión que devuelve la colección sobre la cual iterar, y un segundo parámetro t-as
que proporciona el nombre a usar para el elemento actual de la iteración:
<t t-foreach="[1, 2, 3]" t-as="i" t-key="i"> <p><t t-esc="i"/></p></t>
Se representará como:
<p>1</p><p>2</p><p>3</p>
Al igual que las condiciones, t-foreach
se aplica al elemento que lleva el atributo de la directiva, y
<p t-foreach="[1, 2, 3]" t-as="i" t-key="i"> <t t-esc="i"/></p>
es equivalente al ejemplo anterior.
Se debe hacer una diferencia importante con el comportamiento habitual de QWeb
: Owl requiere la presencia de una directiva t-key
para poder conciliar correctamente las representaciones.
t-foreach
puede iterar sobre cualquier iterable, y también tiene soporte especial para objetos y mapas, expondrá la clave de la iteración actual como el contenido de t-as
, y el valor correspondiente con el mismo nombre y el sufijo _value
.
Además del nombre pasado a través de t-as, t-foreach
proporciona algunas otras variables útiles (nota: $as
será reemplazado con el nombre pasado a t-as
):
$as_value
: el valor de iteración actual, idéntico a$as
para matrices y otros iterables, pero para objetos y mapas, proporciona el valor (donde$as
proporciona la clave)$as_index
: el Ãndice de iteración actual (el primer elemento de la iteración tiene Ãndice 0)$as_first
: si el elemento actual es el primero de la iteración (equivalente a$as_index == 0
)$as_last
: si el elemento actual es el último de la iteración (equivalente a$as_index + 1 == $as_size
), requiere que el tamaño del iterador esté disponible
Estas variables adicionales proporcionadas y todas las nuevas variables creadas en t-foreach
solo están disponibles en el ámbito de t-foreach
. Si la variable existe fuera del contexto de t-foreach
, el valor se copia al final de foreach en el contexto global.
<t t-set="existing_variable" t-value="false"/><!-- existing_variable now False -->
<p t-foreach="Array(3)" t-as="i" t-key="i"> <t t-set="existing_variable" t-value="true"/> <t t-set="new_variable" t-value="true"/> <!-- existing_variable and new_variable now true --></p>
<!-- existing_variable always true --><!-- new_variable undefined -->
Aunque Owl intenta ser lo más declarativo posible, el DOM no expone completamente su estado de forma declarativa en el árbol DOM. Por ejemplo, el estado de desplazamiento, la selección actual del usuario, el elemento enfocado o el estado de una entrada no se establecen como atributos en el árbol DOM. Por eso, utilizamos un algoritmo DOM virtual para asegurarnos de mantener el nodo DOM real en lugar de reemplazarlo por uno nuevo.
Consideremos la siguiente situación: tenemos una lista de dos elementos [{text: "a"}, {text: "b"}]
y los representamos en esta plantilla:
<p t-foreach="items" t-as="item" t-key="item_index"><t t-esc="item.text"/></p>
El resultado serán dos etiquetas <p>
con los textos a
y b
. Ahora, si las intercambiamos y volvemos a renderizar la plantilla, Owl necesita saber cuál es la intención:
- ¿DeberÃa Owl realmente intercambiar los nodos DOM?
- ¿O deberÃa mantener los nodos DOM, pero con un contenido de texto actualizado?
Puede parecer trivial, pero en realidad es importante. Estas dos posibilidades conducen a resultados diferentes en algunos casos. Por ejemplo, si el usuario seleccionó el texto de la primera p
, al intercambiarlos se conservará la selección, pero al actualizar el contenido del texto no.
Hay muchos otros casos en los que esto es importante: etiquetas input
con su valor, clases y animaciones CSS, posición de desplazamiento…
Por lo tanto, la directiva t-key
se utiliza para dar una identidad a un elemento. Permite que Owl comprenda si los distintos elementos de una lista son realmente diferentes o no.
El ejemplo anterior se puede modificar agregando un ID: [{id: 1, text: "a"}, {id: 2, text: "b"}]
. Entonces, la plantilla podrÃa verse asÃ:
<p t-foreach="items" t-as="item" t-key="item.id"><t t-esc="item.text"/></p>
La directiva t-key
es útil para listas (t-foreach
). Una clave debe ser un número o cadena única (los objetos no funcionarán: se convertirán a la cadena "[object Object]"
, que obviamente no es única).
Además, la clave se puede configurar en una etiqueta t
o en sus elementos secundarios. Las siguientes variaciones son todas equivalentes:
<p t-foreach="items" t-as="item" t-key="item.id"> <t t-esc="item.text"/></p>
<t t-foreach="items" t-as="item" t-key="item.id"> <p t-esc="item.text"/></t>
<t t-foreach="items" t-as="item"> <p t-key="item.id" t-esc="item.text"/></t>
Si no hay una directiva t-key
, Owl utilizará el Ãndice como clave predeterminada.
nota: la directiva
t-foreach
solo acepta matrices (listas) u objetos. No funciona con otros iterables, comoSet
. Sin embargo, solo es cuesti%C3%B3n de usar el operador de javascript...
. Por ejemplo:
<t t-foreach="[...items]" t-as="item">...</t>
El operador ...
convertirá el Conjunto
(o cualquier otro iterable) en una lista, que funcionará con Owl QWeb.
Subplantillas
Las plantillas QWeb se pueden usar para la representación de nivel superior, pero también se pueden usar desde otra plantilla (para evitar duplicación o dar nombres a partes de las plantillas), utilizando la directiva t-call
:
<div t-name="other-template"> <p><t t-value="var"/></p></div>
<div t-name="main-template"> <t t-set="var" t-value="owl"/> <t t-call="other-template"/></div>
se representará como <div><p>owl</p></div>
. Este ejemplo muestra que la subplantilla se representa con el contexto de ejecución del padre. La subplantilla está realmente incorporada en la plantilla principal, pero en un subámbito: las variables definidas en la subplantilla no se escapan.
A veces, es posible que desees pasar información a la subplantilla. En ese caso, el contenido del cuerpo de la directiva t-call
está disponible como una variable mágica especial 0
:
<t t-name="other-template"> This template was called with content: <t t-raw="0"/></t>
<div t-name="main-template"> <t t-call="other-template"> <em>content</em> </t></div>
dará como resultado:
<div> This template was called with content: <em>content</em></div>
Esto se puede utilizar para definir variables limitadas a una subplantilla:
<t t-call="other-template"> <t t-set="var" t-value="1"/></t><!-- "var" does not exist here -->
nota: de forma predeterminada, el contexto de representaci%C3%B3n de una subplantilla es simplemente el contexto de representaci%C3%B3n actual. Sin embargo, puede resultar %C3%BAtil poder especificar un objeto espec%C3%ADfico como contexto. Esto se puede hacer utilizando la directiva
t-call-context
:
<t t-call="other-template" t-call-context="obj"/>
Subplantillas dinámicas
La directiva t-call
también se puede utilizar para llamar dinámicamente a una subplantilla, mediante interpolación de cadenas. Por ejemplo:
<div t-name="main-template"> <t t-call="{{template}}"> <em>content</em> </t></div>
AquÃ, el nombre de la plantilla se obtiene del valor plantilla
en el contexto de representación de la plantilla.
Debugging
La implementación de QWeb en JavaScript proporciona dos directivas de depuración útiles:
t-debug
agrega una declaración de depuración durante la representación de la plantilla:
<t t-if="a_test"> <t t-debug=""/></t>
detendrá la ejecución si las herramientas de desarrollo del navegador están abiertas.
t-log
toma un parámetro de expresión, evalúa la expresión durante la representación y registra su resultado con console.log:
<t t-set="foo" t-value="42"/><t t-log="foo"/>
imprimirá 42 en la consola.
Directivas personalizadas
Owl 2 admite la declaración de directivas personalizadas. Para utilizarlas, es necesario configurar un objeto de funciones en la APP Owl:
new App(..., { customDirectives: { test_directive: function (el, value) { el.setAttribute("t-on-click", value); } } });
Las funciones se llamarán cuando se encuentre una directiva personalizada con el nombre de la función. El elemento original se reemplazará por el modificado por la función. Esto:
<div t-custom-test_directive="click" />
será reemplazado por:
<div t-on-click="value"/>
Fragmentos
Owl 2 admite plantillas con una cantidad arbitraria de elementos raÃz o incluso un solo nodo de texto. Por lo tanto, las siguientes plantillas son todas válidas:
hello owl. This is just a text node!
<div>hello</div>
<div>hello</div><div>ola</div>
<div t-if="someCondition"><SomeChildComponent/></div>
<t t-if="someCondition"><SomeChildComponent/></t>
Plantillas en lÃnea
La mayorÃa de las aplicaciones reales definirán sus plantillas en un archivo XML para aprovechar el ecosistema XML y realizar algún procesamiento adicional, como traducirlas. Sin embargo, en algunos casos, es conveniente poder definir una plantilla en lÃnea. Para ello, se puede utilizar la función auxiliar xml
:
const { Component, xml } = owl;
class MyComponent extends Component { static template = xml` <div> <span t-if="somecondition">text</span> <button t-on-click="someMethod">Click</button> </div> `;
...}
mount(MyComponent, document.body);
Esta función simplemente genera una cadena de identificación única y registra la plantilla bajo esa identificación en el interior de Owl, luego devuelve la identificación.
Renderizado de svg
Los componentes de Owl se pueden utilizar para generar gráficos SVG dinámicos:
class Node extends Component { static template = xml` <g> <circle t-att-cx="props.x" t-att-cy="props.y" r="4" fill="black"/> <text t-att-x="props.x - 5" t-att-y="props.y + 18"><t t-esc="props.node.label"/></text> <t t-set="childx" t-value="props.x + 100"/> <t t-set="height" t-value="props.height/(props.node.children || []).length"/> <t t-foreach="props.node.children || []" t-as="child"> <t t-set="childy" t-value="props.y + child_index*height"/> <line t-att-x1="props.x" t-att-y1="props.y" t-att-x2="childx" t-att-y2="childy" stroke="black" /> <Node x="childx" y="childy" node="child" height="height"/> </t> </g> `; static components = { Node };}
class RootNode extends Component { static template = xml` <svg height="180"> <Node node="graph" x="10" y="20" height="180"/> </svg> `; static components = { Node }; graph = { label: "a", children: [ { label: "b" }, { label: "c", children: [{ label: "d" }, { label: "e" }] }, { label: "f", children: [{ label: "g" }] }, ], };}
Este componente RootNode
mostrará una representación SVG en vivo del gráfico descrito por la propiedad graph
. Tenga en cuenta que aquà hay una estructura recursiva: el componente Node
se utiliza a sà mismo como subcomponente.
Nota importante: Owl necesita configurar correctamente el espacio de nombres para cada elemento svg. Dado que Owl compila cada plantilla por separado, no puede determinar fácilmente si una plantilla debe incluirse en un espacio de nombres svg o no. Por lo tanto, Owl depende de una heurÃstica: si una etiqueta es svg
, g
o path
, entonces se considerará como svg. En la práctica, esto significa que cada componente o cada subplantilla (incluida con t-call
) debe tener una de estas etiquetas como etiqueta raÃz.
Restricciones
Tenga en cuenta que las plantillas Owl prohÃben el uso de etiquetas o atributos que comiencen con la cadena block-
. Esta restricción evita la colisión de nombres con el código interno de Owl.
<div><block-1>this will not be accepted by Owl</block-1></div>