¿Por qué este capítulo? Cada característica de OWL que aprenderás más adelante se apoya en el mismo esqueleto de tres archivos que armas aquí — si dominas esta parte, el resto del libro son adiciones, no sorpresas. ¿Qué trata de resolver Odoo con esto? Una estructura predecible y consistente para cada pieza de interfaz personalizada del ecosistema, para que cualquier desarrollador de Odoo pueda abrir la carpeta
static/srcde cualquier addon y saber de inmediato dónde mirar. Aplicación en la vida real: Este es exactamente el andamiaje que reutilizarás el primer día de cualquier proyecto de cliente — un widget de dashboard personalizado, un formulario de portal, una tarjeta de kanban modificada — antes de escribir una sola línea de lógica de negocio.
¡Felicidades por completar los fundamentos! Ahora tienes todo el conocimiento de JavaScript que necesitas para comenzar a construir con OWL. En este capítulo, la teoría termina y la aplicación práctica comienza. Vamos a construir nuestro primer componente clásico "¡Hola, Mundo!".
Este es el momento donde todo lo que has aprendido—clases, módulos, plantillas literales, destructuración y la mentalidad declarativa—se une. Al final de este capítulo, tendrás un componente OWL funcionando dentro de Odoo. Comencemos.
Anatomía de un Componente OWL
Un componente OWL no es un solo archivo. Es un pequeño ecosistema autónomo típicamente compuesto por tres archivos, cada uno con una responsabilidad específica. Esta separación de responsabilidades es un principio central que mantiene tu código limpio, organizado y mantenible.
Piensa en ello como construir una casa:
-
El Archivo JavaScript (
.js): Este es el cerebro de tu componente. Contiene la lógica, el estado, los métodos y define cómo se comporta el componente. Aquí es donde todas tus habilidades modernas de JavaScript entran en juego. -
El Archivo XML (
.xml): Este es el esqueleto. Define la estructura y diseño de la UI de tu componente usando QWeb, el poderoso lenguaje de plantillas de Odoo. Esto determina lo que ven los usuarios. -
El Archivo CSS/SCSS (
.scss): Esta es la piel. Contiene todas las reglas de estilo para hacer que tu componente se vea profesional y coincida con el sistema de diseño de Odoo. (Cubriremos el estilo en detalle en capítulos posteriores.)
Para nuestro primer componente, nos enfocaremos en los primeros dos: el archivo JavaScript y el XML. No te preocupes por el estilo por ahora—primero hagamos que la funcionalidad funcione.
Configurando la Estructura de tu Módulo
Antes de escribir cualquier código, establezcamos una estructura de archivos adecuada. La organización importa, especialmente cuando tus componentes crecen en complejidad.
Crea la siguiente estructura de directorios en tu módulo de Odoo:
my_odoo_module/
|-- __manifest__.py
|-- static/
| |-- src/
| |-- components/
| |-- hello_world/
| |-- hello_world.js
| |-- hello_world.xml
| |-- hello_world.scss (lo añadiremos después)
|-- views/
|-- hello_world_views.xml
¿Por qué esta estructura?
- static/src/components/: Esta es la ubicación estándar para componentes OWL en módulos de Odoo
- Carpetas específicas de componentes: Cada componente tiene su propia carpeta (hello_world/) para mantener los archivos relacionados juntos
- Nomenclatura consistente: Los archivos se nombran según el componente para fácil identificación
El Archivo JavaScript: La Lógica del Componente
Creemos nuestro primer componente. Crea el archivo my_odoo_module/static/src/components/hello_world/hello_world.js:
/** @odoo-module **/
import { Component } from "@odoo/owl";
import { registry } from "@web/core/registry";
export class HelloWorld extends Component {
static template = "my_odoo_module.HelloWorld";
setup() {
console.log("¡El componente HelloWorld se está configurando!");
}
}
// Registrar el componente como acción de cliente para que Odoo pueda mostrarlo
registry.category("actions").add("hello_world_app", HelloWorld);
Analicemos esto línea por línea:
/** @odoo-module **/: Este es un comentario especial que le dice al sistema de assets de Odoo que trate este archivo como un módulo. Es obligatorio para todos los archivos JavaScript en módulos de Odoo. Sin él, tu componente no será reconocido.
import { Component } from "@odoo/owl";: Aquí estamos usando la sintaxis de importación con destructuración que aprendimos. Estamos extrayendo la clase Component de la librería OWL. Esto nos da acceso a todas las características poderosas de los componentes OWL.
export class HelloWorld extends Component { ... }: Esta línea hace varias cosas importantes:
- export: Hace nuestra clase disponible para otras partes del sistema (¿recuerdas los módulos?)
- class HelloWorld: Define nuestra clase de componente con un nombre descriptivo
- extends Component: Hereda toda la funcionalidad de OWL (¿recuerdas la herencia de clases?)
static template = "my_odoo_module.HelloWorld";: Este es el enlace crucial entre nuestra lógica JavaScript y nuestra plantilla XML. El nombre debe ser:
- Único en toda tu instancia de Odoo
- Seguir la convención: nombre_modulo.NombreComponente
- Coincidir exactamente con lo que definimos en el archivo XML
setup() { ... }: Este es el enfoque moderno de OWL 2.0 para la inicialización de componentes. Reemplaza el patrón del constructor anterior y se ejecuta cuando se crea el componente. El console.log nos ayudará a ver cuándo nuestro componente está funcionando.
registry.category("actions").add("hello_world_app", HelloWorld);: Esto registra nuestro componente en el registro de acciones de Odoo bajo el tag hello_world_app. En un momento crearemos una acción de cliente en el servidor cuyo campo tag coincida con este nombre—así es como Odoo sabe qué componente renderizar cuando se dispara la acción.
Entendiendo el Ciclo de Vida del Componente
Incluso en este ejemplo simple, OWL está haciendo mucho detrás de escena:
- Creación del Componente: OWL crea una instancia de nuestra clase
HelloWorld - Ejecución del Setup: Nuestro método
setup()se ejecuta, permitiéndonos inicializar estado y servicios - Renderizado de Plantilla: OWL encuentra la plantilla que especificamos y la renderiza
- Inserción en el DOM: El HTML renderizado se inserta en la página
El Archivo XML: La Plantilla del Componente
Ahora creemos la plantilla que nuestro archivo JavaScript referencia. Crea el archivo my_odoo_module/static/src/components/hello_world/hello_world.xml:
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="my_odoo_module.HelloWorld" owl="1">
<div class="hello-world-component">
<div class="alert alert-info">
<h1 class="alert-heading">
<i class="fa fa-rocket me-2"></i>
¡Hola, Mundo OWL!
</h1>
<p class="mb-0">
¡Este es mi primer componente OWL, y está funcionando perfectamente!
</p>
<hr class="my-3"/>
<p class="mb-0">
<strong>Nombre del Componente:</strong> HelloWorld<br/>
<strong>Plantilla:</strong> my_odoo_module.HelloWorld<br/>
<strong>Framework:</strong> OWL 2.0
</p>
</div>
</div>
</t>
</templates>
Examinemos las partes importantes:
<templates xml:space="preserve">: Este es el envoltorio estándar para todos los archivos de plantilla de Odoo. El xml:space="preserve" asegura que el espacio en blanco en tus plantillas se maneje correctamente.
<t t-name="my_odoo_module.HelloWorld" owl="1">: Aquí es donde ocurre la magia:
- <t>: Una etiqueta de plantilla QWeb que no se renderiza a sí misma, solo su contenido
- t-name="my_odoo_module.HelloWorld": El identificador único que debe coincidir exactamente con la propiedad static template en nuestra clase JavaScript
- owl="1": Este atributo crucial le dice al motor QWeb de Odoo que procese esta plantilla usando el renderizador OWL en lugar del renderizador heredado
El Contenido HTML: Dentro de la etiqueta <t> está el HTML estándar que se convertirá en la estructura DOM real de tu componente. He usado clases de Bootstrap (que Odoo incluye) para que se vea profesional de inmediato.
¿Por qué Esta Estructura de Plantilla?
- HTML Semántico: Usamos elementos HTML5 apropiados y clases de Bootstrap
- Retroalimentación visual clara: El estilo de alerta hace obvio cuándo el componente se renderiza
- Información de depuración: Incluimos los nombres del componente y la plantilla para fácil identificación
- Apariencia profesional: Incluso un componente "Hello World" debería verse bien en Odoo
Registrando el Componente con Odoo
Hemos creado los archivos del componente, pero Odoo aún no sabe sobre ellos. Necesitamos registrarlos con el sistema de assets.
Paso 1: Actualizar el Manifiesto del Módulo
Abre el archivo __manifest__.py de tu módulo y añade los nuevos archivos al diccionario assets:
# En __manifest__.py
{
'name': 'Mi Módulo Hello World OWL',
'version': '1.0',
'depends': ['base', 'web'],
'data': [
'views/hello_world_views.xml',
],
'assets': {
'web.assets_backend': [
'my_odoo_module/static/src/components/hello_world/hello_world.js',
'my_odoo_module/static/src/components/hello_world/hello_world.xml',
],
},
'installable': True,
'auto_install': False,
}
Puntos importantes sobre assets:
- web.assets_backend: Este bundle se carga en el backend de Odoo (donde viven la mayoría de las aplicaciones de negocio)
- El orden importa: Los archivos JavaScript generalmente deberían venir antes que los archivos XML
- Precisión en las rutas: Revisa dos veces las rutas de tus archivos—los errores tipográficos aquí causarán fallas silenciosas
Paso 2: Crear una Vista para Mostrar el Componente
Crea un archivo de vista en my_odoo_module/views/hello_world_views.xml:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- Definir una acción de cliente para mostrar nuestro componente.
El "tag" debe coincidir con el nombre usado en registry.category("actions").add(...) -->
<record id="action_hello_world" model="ir.actions.client">
<field name="name">Hola Mundo OWL</field>
<field name="tag">hello_world_app</field>
<field name="target">current</field>
</record>
<!-- Añadir un elemento de menú para que los usuarios puedan encontrar tu componente -->
<menuitem id="menu_hello_world"
name="Hola Mundo OWL"
action="action_hello_world"
parent="base.menu_administration"
sequence="100"/>
</data>
</odoo>
Entendiendo esta estructura:
Acción de Cliente: A diferencia de las vistas regulares de Odoo (formulario, lista, etc.), los componentes OWL a menudo usan "acciones de cliente"—acciones especiales que renderizan aplicaciones JavaScript personalizadas en lugar de vistas estándar.
El campo tag es el enlace: Cuando el usuario dispara esta acción, Odoo busca hello_world_app en el registro de acciones de JavaScript y encuentra el componente HelloWorld que registramos ahí. No se necesita ninguna plantilla del lado del servidor—la propia plantilla QWeb del componente se encarga del renderizado.
Integración de Menú: El <menuitem> les da a los usuarios una forma de acceder a tu componente a través del sistema de menú estándar de Odoo.
Probando tu Componente
¡Ahora el momento de la verdad! Vamos a hacer funcionar tu componente:
Paso 1: Instalar/Actualizar tu Módulo
# Si este es un módulo nuevo
./odoo-bin -d tu_base_de_datos -i my_odoo_module --dev=all
# Si estás actualizando un módulo existente
./odoo-bin -d tu_base_de_datos -u my_odoo_module --dev=all
La bandera --dev=all es crucial—asegura que tus cambios de JavaScript y XML se carguen inmediatamente sin necesidad de reiniciar el servidor.
Paso 2: Navegar a tu Componente
- Inicia sesión en tu instancia de Odoo
- Ve a Configuración (o donde hayas puesto tu elemento de menú)
- Haz clic en "Hola Mundo OWL"
Si todo funcionó correctamente, ¡deberías ver tu hermoso componente renderizado con el estilo de alerta de Bootstrap!
Paso 3: Verificar en las Herramientas de Desarrollo del Navegador
Abre las DevTools de tu navegador (F12) y:
- Revisa la Consola: Deberías ver el mensaje "¡El componente HelloWorld se está configurando!"
- Inspecciona los Elementos: Deberías ver la estructura HTML de tu plantilla
- Revisa la pestaña Network: Verifica que tus archivos
.jsy.xmlse cargaron
Problemas Comunes y Soluciones
Error "Component not found":
- Verifica que tu propiedad static template coincida exactamente con el t-name en tu XML
- Verifica que ambos archivos estén listados en __manifest__.py
- Asegúrate de haber actualizado tu módulo
Error "Template not found":
- Confirma que el atributo owl="1" esté presente en tu plantilla
- Revisa errores tipográficos en el nombre de la plantilla
- Verifica que el archivo XML sea válido (sin errores de sintaxis)
El componente no aparece:
- Revisa la consola del navegador para errores de JavaScript
- Verifica que tu acción de cliente y elemento de menú estén definidos correctamente
- Asegúrate de estar ejecutando con --dev=all
El estilo se ve mal: - Odoo incluye Bootstrap por defecto, así que nuestras clases deberían funcionar - Intenta inspeccionar los elementos para ver qué CSS se está aplicando - Recuerda que no hemos añadido CSS personalizado aún—eso viene en capítulos posteriores
Lo que has Logrado
¡Felicidades! Acabas de construir tu primer componente OWL e integrarlo en Odoo. Este ejemplo aparentemente simple demuestra varios conceptos cruciales:
Arquitectura de Componentes: Has visto cómo la lógica JavaScript y las plantillas XML trabajan juntas
JavaScript Moderno en la Práctica: Has usado import, export, class y extends en una aplicación real
Integración con Odoo: Entiendes cómo los componentes OWL encajan en el sistema de módulos de Odoo
Flujo de Trabajo de Desarrollo: Sabes cómo crear, registrar y probar componentes
Fundamentos de Depuración: Tienes los conceptos básicos para solucionar problemas cuando las cosas salen mal
TL;DR: Un componente OWL es una clase JS (static template + setup()) emparejada con una plantilla XML QWeb (t-name, owl="1") cuyos nombres deben coincidir exactamente; se registra con registry.category("actions").add(tag, Component) y un registro ir.actions.client que comparte ese tag.
Pruébalo tú mismo: El ejemplo de este capítulo está disponible como addon instalable de Odoo 19: simplifyit_owl_book_ch5_ex1. Las instrucciones de instalación están en el README del repositorio.
Ejercicios
- Personaliza el saludo. Cambia el componente para que salude a un nombre específico (ej.
"¡Hola, Ana!") agregando una propiedad JavaScript simple ensetup()y mostrándola cont-escen la plantilla. - Rómpelo a propósito. Renombra el
t-nameen tu archivo XML para que ya no coincida constatic templateen el archivo JavaScript, recarga la página, y lee el error exacto que Odoo muestra en la consola. Luego arréglalo. Reconocer este mensaje de error te ahorrará tiempo más adelante. - Añade una segunda acción. Añade un botón que llame a un nuevo método que registre un mensaje en la consola con
console.log, y verifícalo en la pestaña Console de las DevTools del navegador del Capítulo 2.
¿Qué Sigue?
Ahora mismo este componente es estático — muestra el mismo texto sin importar nada. El Capítulo 6 lo hace dinámico: aprenderás las directivas QWeb (t-esc, t-if, t-foreach y más) que permiten que tu plantilla reaccione a los datos que vienen del lado JavaScript.