Saltearse al contenido

Componente

Un componente Owl es una clase pequeña que representa una parte de la interfaz de usuario. Forma parte de un árbol de componentes y tiene un entorno environment (env), que se propaga desde un componente principal a sus componentes secundarios.

Los componentes OWL se definen mediante la herencia de la clase Component. Por ejemplo, así es como se podría implementar un componente Counter:

const { Component, xml, useState } = owl;
class Counter extends Component {
static template = xml`
<button t-on-click="increment">
Click Me! [<t t-esc="state.value"/>]
</button>`;
state = useState({ value: 0 });
increment() {
this.state.value++;
}
}

En este ejemplo, utilizamos el ayudante xml para definir plantillas en línea y el gancho useState, que devuelve una versión reactiva de su argumento (consulte la página sobre reactividad).

Propiedades y métodos

La clase Componente tiene una API muy pequeña.

  • env (object): el componente environment

  • props (object): Este es un objeto que contiene todas las props dadas por el componente padre a un componente hijo.

    Tenga en cuenta que las “props” pertenecen al elemento principal, no al componente. Por lo tanto, el componente nunca debería modificarlos (de lo contrario, corre el riesgo de que se produzcan efectos no deseados, ya que el elemento principal puede no estar al tanto del cambio).

    El componente padre puede modificar dinámicamente las props. En ese caso, el componente pasará por los siguientes métodos de ciclo de vida: willUpdateProps, willPatch y patched.

  • render(deep[=false]): Al llamar a este método directamente se producirá una nueva renderización. Tenga en cuenta que con el sistema de reactividad, debería ser poco frecuente tener que hacerlo manualmente. Además, la operación de renderización es asincrónica, por lo que el DOM solo se actualizará un poco más tarde (en el siguiente cuadro de animación, si ningún componente retrasa la renderización).

    De forma predeterminada, el renderizado iniciado por este método se detendrá en cada componente secundario si sus propiedades son (superficiales) iguales. Para forzar un renderizado a actualizar todos los componentes secundarios, se puede usar el argumento opcional deep. Tenga en cuenta que el valor del argumento deep debe ser un valor booleano, no un valor verdadero.

Propiedades estáticas

  • template (string): Este es el nombre de la plantilla que representará el componente. Tenga en cuenta que hay un asistente xml para facilitar la definición de una plantilla en línea.

  • components (object, optional): Si se proporciona, este es un objeto que contiene las clases de cualquier subcomponente que necesite la plantilla.

    class ParentComponent extends owl.Component {
    static components = { SubComponent };
    }
  • props (object, optional): Si se proporciona, este es un objeto que describe el tipo y la forma de las propiedades (reales) proporcionadas al componente. Si el modo Owl es dev, se utilizará para validar las propiedades cada vez que se cree o actualice el componente. Consulte Validación de propiedades para obtener más información.

    class Counter extends owl.Component {
    static props = {
    initialValue: Number,
    optional: true,
    };
    }
  • defaultProps (object, optional): Si se proporciona, este objeto define valores predeterminados para las propiedades (de nivel superior). Siempre que se proporcionen props al objeto, se modificarán para agregar el valor predeterminado (si falta). Tenga en cuenta que no cambia el objeto inicial, se creará un nuevo objeto en su lugar. Consulte propiedades predeterminadas para obtener más información.

    class Counter extends owl.Component {
    static defaultProps = {
    initialValue: 0,
    };
    }

Ciclo de Vida

Un sistema de componentes sólido y robusto necesita un sistema de ciclo de vida completo para ayudar a los desarrolladores a escribir componentes. A continuación, se incluye una descripción completa del ciclo de vida de un componente Owl:

MétodoHookDescripción
setupnoneconfiguración
willStartonWillStartasíncrono, antes de la primera renderización
willRenderonWillRenderJusto antes de que se renderice el componente
renderedonRenderedJusto después de que se renderiza el componente
mountedonMountedJusto después de que el componente se renderiza y se agrega al DOM
willUpdatePropsonWillUpdatePropsasíncrono, antes de actualizar las propiedades
willPatchonWillPatchJusto antes de que se parchee el DOM
patchedonPatchedJusto después de que se parchea el DOM
willUnmountonWillUnmountJusto antes de eliminar el componente del DOM
willDestroyonWillDestroyJusto antes de que se destruya el componente
erroronErrorCaptura y gestiona errores (Ver Manejo de Errores)

setup

setup se ejecuta justo después de que se construye el componente. Es un método de ciclo de vida, muy similar al constructor, excepto que no recibe ningún argumento.

Es el lugar adecuado para llamar a las funciones de gancho. Tenga en cuenta que una de las principales razones para tener el gancho setup en el ciclo de vida del componente es hacer posible aplicarle parches. Es una necesidad común en el ecosistema de Odoo.

setup() {
useSetupAutofocus();
}

willStart

willStart es un gancho asincrónico que se puede implementar para realizar alguna acción (la mayoría de las veces asincrónica) antes de la representación inicial de un componente.

Se llamará exactamente una vez antes de la renderización inicial. Es útil en algunos casos, por ejemplo, para cargar recursos externos (como una biblioteca JS) antes de que se renderice el componente. Otro caso de uso es cargar datos desde un servidor.

El gancho onWillStart se utiliza para registrar una función que se ejecutará en este momento:

setup() {
onWillStart(async () => {
this.data = await this.loadData()
});
}

En este punto, el componente aún no se ha renderizado. Tenga en cuenta que el código willStart lento ralentizará la renderización de la interfaz de usuario. Por lo tanto, se debe tener cuidado para que este método sea lo más rápido posible.

Tenga en cuenta que si hay más de una devolución de llamada onWillStart registrada, todas se ejecutarán en paralelo.

willRender

No es habitual, pero puede suceder, que sea necesario ejecutar código justo antes de que se renderice un componente (más precisamente, cuando se ejecuta su función de plantilla compilada). Para ello, se puede utilizar el gancho onWillRender:

setup() {
onWillRender(() => {
// do something
});
}

El gancho willRender se llama justo antes de renderizar las plantillas, primero las principales y luego las secundarias.

rendered

No es habitual, pero puede suceder, que sea necesario ejecutar código justo después de que se renderiza un componente (más precisamente, cuando se ejecuta su función de plantilla compilada). Para ello, se puede utilizar el gancho onRendered:

setup() {
onRendered(() => {
// do something
});
}

Los ganchos rendered se llaman justo después de renderizar las plantillas, primero el padre y luego los hijos. Tenga en cuenta que en este momento, es posible que el DOM real aún no exista (si es la primera renderización) o que aún no se haya actualizado. Este será el DOM en el siguiente cuadro de animación tan pronto como todos los componentes estén listos.

mounted

El gancho mounted se llama cada vez que se adjunta un componente al DOM, después de la representación inicial. En este punto, el componente se considera activo. Este es un buen lugar para agregar algunos oyentes o para interactuar con el DOM, si el componente necesita realizar alguna medida, por ejemplo.

Es lo opuesto de “willUnmount”. Si un componente ha sido montado, siempre será desmontado en algún momento en el futuro.

El método montado se llamará de forma recursiva en cada uno de sus hijos. Primero, los hijos, luego los padres.

Está permitido (pero no se recomienda) modificar el estado en el gancho mounted. Si lo hace, se volverá a renderizar, lo que no será perceptible para el usuario, pero ralentizará ligeramente el componente.

El gancho onMounted se utiliza para registrar una función que se ejecutará en este momento:

setup() {
onMounted(() => {
// do something here
});
}

willUpdateProps

willUpdateProps es un gancho asincrónico, que se llama justo antes de que se establezcan nuevas propiedades. Esto es útil si el componente necesita realizar una tarea asincrónica, dependiendo de las propiedades (por ejemplo, suponiendo que las propiedades son algún Id de registro, obteniendo los datos del registro).

El gancho onWillUpdateProps se utiliza para registrar una función que se ejecutará en este momento:

setup() {
onWillUpdateProps(nextProps => {
return this.loadData({id: nextProps.id});
});
}

Tenga en cuenta que recibe las siguientes propiedades para el componente.

Este gancho no se llama durante la primera renderización (pero se llama a willStart y realiza una tarea similar). Además, como la mayoría de los ganchos, se llama en el orden habitual: primero los padres, luego los hijos.

willPatch

El gancho willPatch se llama justo antes de que comience el proceso de aplicación de parches al DOM. No se llama en la representación inicial. Esto resulta útil para leer información del DOM. Por ejemplo, la posición actual de la barra de desplazamiento.

Tenga en cuenta que aquí no se permite modificar el estado. Este método se llama justo antes de un parche DOM real y solo está pensado para usarse para guardar algún estado DOM local. Además, no se llamará si el componente no está en el DOM.

El gancho onWillPatch se utiliza para registrar una función que se ejecutará en este momento:

setup() {
onWillPatch(() => {
this.scrollState = this.getScrollSTate();
});
}

willPatch se llama en el orden habitual padre -> hijos.

patched

Este gancho se llama siempre que un componente realmente actualizó su DOM (probablemente a través de un cambio en su estado/propiedades o entorno).

Este método no se llama en la renderización inicial. Es útil para interactuar con el DOM (por ejemplo, a través de una biblioteca externa) siempre que se haya aplicado un parche al componente. Tenga en cuenta que este gancho no se llamará si el componente no está en el DOM.

El gancho onPatched se utiliza para registrar una función que se ejecutará en este momento:

setup() {
onPatched(() => {
this.scrollState = this.getScrollSTate();
});
}

Es posible actualizar el estado del componente en este gancho, pero no se recomienda. Hay que tener cuidado, porque las actualizaciones aquí crearán una representación adicional, que a su vez provocará otras llamadas al método patched. Por lo tanto, debemos tener especial cuidado para evitar ciclos infinitos.

Al igual que mounted, el gancho patched se llama en el orden: primero los hijos, luego los padres.

willUnmount

willUnmount es un gancho que se llama cada vez justo antes de que se desmonte un componente del DOM. Este es un buen lugar para eliminar oyentes, por ejemplo.

El gancho onWillUnmount se utiliza para registrar una función que se ejecutará en este momento:

setup() {
onMounted(() => {
// add some listener
});
onWillUnmount(() => {
// remove listener
});
}

Este es el método opuesto a mounted. Tenga en cuenta que si se destruye un componente antes de montarlo, es posible que no se llame al método willUnmount.

Los ganchos padres willUnmount se llamarán antes que los hijos.

willDestroy

A veces, los componentes necesitan realizar alguna acción en la configuración y limpiarla cuando están inactivos. Sin embargo, el gancho willUnmount no es apropiado para la operación de limpieza, ya que el componente puede destruirse antes de que se haya montado. El gancho willDestroy es útil en esa situación, ya que siempre se llama.

El gancho onWillDestroy se utiliza para registrar una función que se ejecutará en este momento:

setup() {
onWillDestroy(() => {
// do some cleanup
});
}

Los ganchos willDestroy se llaman primero en los hijos y luego en los padres.

onError

Lamentablemente, puede ocurrir que los componentes fallen durante la ejecución. Esta es una triste realidad y es por eso que Owl necesita proporcionar una forma de manejar estos errores.

El gancho onError es útil cuando necesitamos interceptar y reaccionar adecuadamente ante errores que ocurren en algunos subcomponentes. Consulte la página sobre manejo de errores para obtener más detalles.

setup() {
onError(() => {
// do something
});
}

Subcomponentes

Es conveniente definir un componente utilizando otros (sub)componentes. Esto se llama composición y es muy eficaz en la práctica. Para hacerlo en Owl, uno puede simplemente usar una etiqueta que comience con una letra mayúscula en su plantilla y registrar la clase del subcomponente en su objeto estático components:

class Child extends Component {
static template = xml`<div>child component <t t-esc="props.value"/></div>`;
}
class Parent extends Component {
static template = xml`
<div>
<Child value="1"/>
<Child value="2"/>
</div>`;
static components = { Child };
}

Este ejemplo también muestra cómo se puede pasar información del componente principal al componente secundario, como propiedades. Consulte la sección props para obtener más información.

Subcomponentes dinámicos

No es habitual, pero a veces necesitamos un nombre de componente dinámico. En este caso, también se puede utilizar la directiva t-component para aceptar valores dinámicos. Esta debe ser una expresión que evalúe una clase de componente. Por ejemplo:

class A extends Component {
static template = xml`<div>child a</div>`;
}
class B extends Component {
static template = xml`<span>child b</span>`;
}
class Parent extends Component {
static template = xml`<t t-component="myComponent"/>`;
state = useState({ child: "a" });
get myComponent() {
return this.state.child === "a" ? A : B;
}
}

status helper

A veces resulta conveniente tener una forma de averiguar en qué estado se encuentra actualmente un componente. Para ello, se puede utilizar el asistente status:

const { status } = owl;
// assume component is an instance of a Component
console.log(status(component));
// logs either:
// - 'new', if the component is new and has not been mounted yet
// - 'mounted', if the component is currently mounted
// - 'cancelled', if the component has not been mounted yet but will be destroyed soon
// - 'destroyed' if the component is currently destroyed