Frontend Reactivo con OWL y Creación de Widgets
Desde Odoo 14 y de forma consolidada en Odoo 16, 17 y 18, todo el cliente web se sustenta sobre OWL (Odoo Web Library), un framework moderno de JavaScript reactivo basado en componentes y Virtual DOM, diseñado específicamente para satisfacer las demandas de rendimiento de aplicaciones empresariales complejas.
1. Fundamentos y Arquitectura de OWL 2.0 #
A diferencia de los frameworks externos como React o Vue, OWL es una librería ultraligera desarrollada por Odoo que combina:
- Sintaxis Declarativa de Plantillas QWeb: Uso de directivas en XML (
t-if,t-foreach,t-on-click,t-model,t-ref). - Estado Reactivo (
useState): Al modificar una propiedad del estado, el componente actualiza únicamente los nodos del DOM alterados. - Sistema de Hooks: Gestión de ciclo de vida (
onWillStart,onMounted,onWillUpdateProps,useRef).
1┌──────────────────────────────────────────────────────────────────────────────────┐ 2│ EL FLUJO REACTIVO DE OWL │ 3├──────────────────────────────────────────────────────────────────────────────────┤ 4│ Modelo Odoo (Python) ──► ORM Backend ──► Web Client RPC ──► Componente OWL (JS) │ 5│ │ (useState) │ 6│ DOM del Navegador (HTML5) ◄── Virtual DOM Diff ◄── QWeb Template ┘ │ 7└──────────────────────────────────────────────────────────────────────────────────┘
2. Creación Paso a Paso de un Widget/Campo Personalizado #
Vamos a construir un widget visual de Semáforo de Prioridad (Priority Badge) para usar en vistas de formulario.
El Componente JavaScript (static/src/components/priority_badge/priority_badge.js): #
1/** @odoo-module **/ 2 3import { Component, useState } from "@odoo/owl"; 4import { registry } from "@web/core/registry"; 5import { standardFieldProps } from "@web/views/fields/standard_field_props"; 6 7export class PriorityBadgeField extends Component { 8 static template = "biblioteca.PriorityBadgeField"; 9 static props = { 10 ...standardFieldProps, 11 }; 12 13 setup() { 14 this.state = useState({ 15 isHovered: false, 16 }); 17 } 18 19 get colorClass() { 20 const val = this.props.record.data[this.props.name]; 21 if (val === 'high') return 'bg-danger text-white'; 22 if (val === 'medium') return 'bg-warning text-dark'; 23 return 'bg-success text-white'; 24 } 25 26 setPriority(newPriority) { 27 this.props.record.update({ [this.props.name]: newPriority }); 28 } 29} 30 31// Registro del campo en el sistema de campos de Odoo 32export const priorityBadgeField = { 33 component: PriorityBadgeField, 34 supportedTypes: ["selection"], 35}; 36 37registry.category("fields").add("priority_badge", priorityBadgeField);
La Plantilla XML de OWL (static/src/components/priority_badge/priority_badge.xml): #
1<?xml version="1.0" encoding="UTF-8"?> 2<templates xml:space="preserve"> 3 <t t-name="biblioteca.PriorityBadgeField"> 4 <div class="d-inline-flex gap-1 align-items-center"> 5 <span t-attf-class="badge rounded-pill p-2 #{colorClass}"> 6 <t t-esc="props.record.data[props.name] || 'Normal'"/> 7 </span> 8 <button type="button" class="btn btn-sm btn-outline-secondary" t-on-click="() => this.setPriority('high')"> 9 Alta 10 </button> 11 <button type="button" class="btn btn-sm btn-outline-secondary" t-on-click="() => this.setPriority('low')"> 12 Baja 13 </button> 14 </div> 15 </t> 16</templates>
3. Registro de Assets en el Manifiesto (__manifest__.py) #
Para que Odoo compile e inyecte los archivos JS y XML en el backend:
1{ 2 'name': 'Biblioteca', 3 'version': '17.0.1.0.0', 4 'depends': ['web', 'base'], 5 'assets': { 6 'web.assets_backend': [ 7 'biblioteca/static/src/components/priority_badge/**/*', 8 ], 9 }, 10 'installable': True, 11}
4. Uso del Widget en Vistas de Formulario #
Una vez registrado en registry.category("fields"), se aplica directamente con el atributo widget="priority_badge":
1<record id="view_library_book_form" model="ir.ui.view"> 2 <field name="name">library.book.form</field> 3 <field name="model">library.book</field> 4 <field name="arch" type="xml"> 5 <form> 6 <sheet> 7 <group> 8 <field name="name"/> 9 <!-- Campo Selection renderizado con nuestro componente OWL --> 10 <field name="priority" widget="priority_badge"/> 11 </group> 12 </sheet> 13 </form> 14 </field> 15</record>
Resumen del tema
Conceptos clave #
- Framework OWL (Odoo Web Library): motor frontend reactivo basado en Virtual DOM, componentes modulares y directivas XML (
t-name,t-if,t-on-click). - Estado Reactivo (
useState): sincronización automática bidireccional entre la interfaz web y los datos del modelo sin recargar la página. - Registro Global de Odoo (
@web/core/registry): sistema de extensibilidad para incorporar nuevos campos (category("fields")), vistas y acciones de cliente. - Paquete de Assets (
web.assets_backend): compilación y empaquetado de componentes JS, plantillas XML y estilos SCSS definidos en__manifest__.py.
Qué debes recordar #
OWL es el framework reactivo que potencia el frontend de Odoo moderno, permitiendo crear Widgets y Componentes UI reutilizables registrados en web.assets_backend.