Frontend Reactivo con OWL y Creación de Widgets
El cliente web de Odoo 20 utiliza 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. Componentes y reactividad de OWL #
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 if (!this.props.readonly) { 28 return this.props.record.update({ [this.props.name]: newPriority }); 29 } 30 } 31} 32 33// Registro del campo en el sistema de campos de Odoo 34export const priorityBadgeField = { 35 component: PriorityBadgeField, 36 supportedTypes: ["selection"], 37}; 38 39registry.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 t-if="!props.readonly" type="button" class="btn btn-sm btn-outline-secondary" t-on-click="() => this.setPriority('high')"> 9 Alta 10 </button> 11 <button t-if="!props.readonly" 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) #
Añade esta clave al diccionario del manifiesto existente, conservando data, dependencias y demás claves:
1'assets': { 2 'web.assets_backend': [ 3 'biblioteca/static/src/components/priority_badge/**/*', 4 ], 5},
4. Uso del Widget en Vistas de Formulario #
El campo priority ya existe en el modelo de la lección de ORM. Guarda esta vista en views/priority_widget.xml y añádela al manifiesto después de la vista base. Una vez registrado en registry.category("fields"), se aplica directamente con el atributo widget="priority_badge":
1<odoo> 2 <record id="view_library_book_priority_widget" model="ir.ui.view"> 3 <field name="name">library.book.priority.widget</field> 4 <field name="model">library.book</field> 5 <field name="inherit_id" ref="view_library_book_form"/> 6 <field name="arch" type="xml"> 7 <xpath expr="//field[@name='priority']" position="attributes"> 8 <attribute name="widget">priority_badge</attribute> 9 </xpath> 10 </field> 11 </record> 12</odoo>
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): estado local reactivo;record.update()comunica los cambios del campo al formulario y el guardado los persiste. - 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.