[comment]: # aside:2

[comment]: # ({49595c0a-680e771f})
# Presentación

Esta página describe los componentes que se pueden utilizar para crear una vista de presentación de widget.
La vista de presentación del widget es la parte del widget que recibe los datos según su [configuración](/configuration) y los muestra en el panel en un contenedor.

La vista de presentación consta de tres partes:

-   [Acción del widget](#widget-action)
-   [Vista del widget](#widget-view)
-   [JavaScript](#javascript)

[comment]: # ({/49595c0a-680e771f})

[comment]: # ({12d91455-59d8ff17})
### Acción de widget

La clase de acción de widget (*WidgetView*) contiene métodos para operaciones con widgets en el modo de vista de presentación.
La mayoría de las acciones de widget utilizan y/o extienden la clase de controlador por defecto *CControllerDashboardWidgetView*.

La clase de acción de widget debe ubicarse en el directorio *actions* y especificarse en el parámetro [*actions*](/devel/modules/file_structure/manifest#actions) (*actions/widget.{id}.view/class*) en el archivo *manifest.json*.

**Ejemplo de actions/WidgetView.php (implementado en el widget nativo de Zabbix [*Información del sistema*](/manual/web_interface/frontend_sections/dashboards/widgets/system))**

```php
class WidgetView extends CControllerDashboardWidgetView {

    protected function doAction(): void {
        $this->setResponse(new CControllerResponseData([
            'name' => $this->getInput('name', $this->widget->getDefaultName()),
            'system_info' => CSystemInfoHelper::getData(),
            'info_type' => $this->fields_values['info_type'],
            'user_type' => CWebUser::getType(),
            'user' => [
                'debug_mode' => $this->getDebugMode()
            ]
        ]));
    }
}
```

[comment]: # ({/12d91455-59d8ff17})

[comment]: # ({c06252a9-962bf489})
### Vista del widget

La clase de vista del widget (*CWidgetView*) es responsable de construir la vista de presentación del widget.

La clase de vista del widget debe ubicarse en el directorio *views*.
Si el archivo que contiene la clase de vista del widget tiene un nombre diferente al predeterminado (*widget.view.php*), entonces debe especificarse en el parámetro [*actions*](/devel/modules/file_structure/manifest#actions) del archivo *manifest.json* (*actions/widget.{id}.view/view*).

**Ejemplo de views/widget.view.php**

```php
<?php

/**
 * Mi vista personalizada de widget.
 *
 * @var CView $this
 * @var array $data
 */

(new CWidgetView($data))
    ->addItem(
        new CTag('h1', true, $data['name'])
    )
    ->show();
```

[comment]: # ({/c06252a9-962bf489})

[comment]: # ({f4f943a3-9c902f1e})
### JavaScript

La clase JavaScript es responsable de determinar el comportamiento del widget, como actualizar los datos del widget, cambiar el tamaño del widget, mostrar los elementos del widget, etc.

Todas las operaciones de JavaScript utilizan y/o extienden la clase base de JavaScript de todos los widgets del panel - *CWidget*.
La clase *CWidget* contiene un conjunto de métodos con la implementación predeterminada para el comportamiento del widget.
Dependiendo de la complejidad del widget, estos métodos pueden utilizarse tal cual o extenderse.

La clase *CWidget* contiene los siguientes métodos:

-   Métodos que definen el ciclo de vida del widget: *onInitialize()*, *onStart()*, *onActivate()*, *onDeactivate()*, *onDestroy()*, *onEdit()*.
-   Métodos que gestionan la actualización y visualización de los datos del widget: *promiseUpdate()*, *getUpdateRequestData()*, *processUpdateResponse(response)*, *processUpdateErrorResponse(error)*, *setContents(response)*.
-   Métodos que modifican la apariencia del widget: *onResize()*, *hasPadding()*.

La clase JavaScript debe ubicarse en el directorio *assets/js* y especificarse en el parámetro [*assets*](/devel/modules/file_structure/manifest#assets) (*assets/js*) en el archivo *manifest.json*.

[comment]: # ({/f4f943a3-9c902f1e})

[comment]: # ({83b2097c-be08c6bd})
##### Métodos del ciclo de vida

Los métodos del ciclo de vida del widget son invocados por el panel, y en diferentes etapas del ciclo de vida del widget durante su existencia dentro del panel.

[comment]: # ({/83b2097c-be08c6bd})

[comment]: # ({af2ec67e-bbc2c368})
El método ***onInitialize()*** define el estado inicial y/o los valores del widget, sin realizar ninguna manipulación de HTML o datos.
Este método se invoca cuando se crea un widget (se instancia un objeto widget), normalmente al añadir el widget a una página de panel o al cargar la página del panel.

Ejemplo:

```js
onInitialize() {
    this._time_offset = 0;
    this._interval_id = null;
    this._clock_type = CWidgetClock.TYPE_ANALOG;
    this._time_zone = null;
    this._show_seconds = true;
    this._time_format = 0;
    this._tzone_format = 0;
    this._show = [];
    this._has_contents = false;
    this._is_enabled = true;
}
```

[comment]: # ({/af2ec67e-bbc2c368})

[comment]: # ({d89d02f3-9d16c8d2})
El método ***onStart()*** define la estructura HTML del widget, sin realizar ninguna manipulación de datos.
Este método se invoca antes de la primera activación de la página del panel, es decir, antes de que el panel y sus widgets se muestren completamente al usuario.

Ejemplo:

```js
onStart() {
    this._events.resize = () => {
        const padding = 25;
        const header_height = this._view_mode === ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER
            ? 0
            : this._header.offsetHeight;

        this._target.style.setProperty(
            '--content-height',
            `${this._cell_height * this._pos.height - padding * 2 - header_height}px`
        );
    }
}
```

[comment]: # ({/d89d02f3-9d16c8d2})

[comment]: # ({02fa6174-eb9b91db})
El método ***onActivate()*** hace que el widget esté activo e interactivo al habilitar los listeners de eventos personalizados (para responder a acciones del usuario) e iniciar el ciclo de actualización del widget (para mantener su contenido actualizado).
Este método se invoca cuando la página del panel se activa, es decir, cuando se muestra completamente en la interfaz de usuario.

Tenga en cuenta que antes de que se invoque el método *onActivate()*, el widget está en estado inactivo (`WIDGET_STATE_INACTIVE`).
Después de una invocación exitosa, el widget pasa al estado activo (`WIDGET_STATE_ACTIVE`).
En el estado activo, el widget es receptivo, escucha eventos, actualiza su contenido periódicamente y puede interactuar con otros widgets.

Ejemplo:

```js
onActivate() {
    this._startClock();

    this._resize_observer = new ResizeObserver(this._events.resize);
    this._resize_observer.observe(this._target);
}
```

[comment]: # ({/02fa6174-eb9b91db})

[comment]: # ({4464acae-1b9abef7})
El método ***onDeactivate()*** detiene cualquier actividad e interactividad del widget desactivando los listeners de eventos personalizados y deteniendo el ciclo de actualización del widget.
Este método se invoca cuando la página del panel se desactiva, es decir, se cambia o elimina, o cuando el widget se elimina de la página del panel.

Tenga en cuenta que antes de que se invoque el método *onDeactivate()*, el widget está en el estado activo (`WIDGET_STATE_ACTIVE`).
Después de una invocación exitosa, el widget pasa al estado inactivo (`WIDGET_STATE_INACTIVE`).

Ejemplo:

```js
onDeactivate() {
    this._stopClock();
    this._resize_observer.disconnect();
}
```

[comment]: # ({/4464acae-1b9abef7})

[comment]: # ({a26add25-06e3db00})
El método ***onDestroy()*** realiza tareas de limpieza antes de que el widget sea eliminado del panel,
lo que puede incluir cerrar una conexión a la base de datos que se estableció durante la inicialización del widget,
limpiar datos temporales para liberar memoria del sistema y evitar fugas de recursos,
cancelar el registro de escuchas de eventos relacionados con eventos de cambio de tamaño o clics de botones para evitar el manejo innecesario de eventos y fugas de memoria, etc.
Este método se invoca cuando se elimina el widget o la página del panel que lo contiene.

Tenga en cuenta que antes de que se invoque el método *onDestroy()*, un widget en estado activo (`WIDGET_STATE_ACTIVE`) siempre se desactiva con la invocación del método *onDeactivate()*.

Ejemplo:

```js
onDestroy() {
    if (this._filter_widget) {
        this._filter_widget.off(CWidgetMap.WIDGET_NAVTREE_EVENT_MARK, this._events.mark);
        this._filter_widget.off(CWidgetMap.WIDGET_NAVTREE_EVENT_SELECT, this._events.select);
    }
}
```

[comment]: # ({/a26add25-06e3db00})

[comment]: # ({2a2b0a8f-5e5aaf59})
El método ***onEdit()*** define la apariencia y el comportamiento del widget cuando el dashboard pasa al modo de edición.
Este método se invoca cuando el dashboard pasa al modo de edición, normalmente cuando un usuario interactúa con el botón *Editar* del widget o el botón *Editar dashboard* del dashboard.

Ejemplo:

```js
onEdit() {
    this._deactivateGraph();
}
```

[comment]: # ({/2a2b0a8f-5e5aaf59})

[comment]: # ({1e54f2e8-87f28aae})
##### Métodos del proceso de actualización

Los métodos del proceso de actualización del widget son responsables de recuperar datos actualizados desde el servidor Zabbix o cualquier otra fuente de datos y mostrarlos en el widget.

[comment]: # ({/1e54f2e8-87f28aae})

[comment]: # ({fa94eb63-c743ab46})
El método ***promiseUpdate()*** inicia el proceso de actualización de datos recuperando datos, normalmente utilizando solicitudes web o llamadas a API.
Este método se invoca cuando se muestra una página de panel y periódicamente después, hasta que la página de panel se cambia a otra página de panel.

A continuación se muestra un ejemplo de la implementación por defecto del método *promiseUpdate()* utilizado por la mayoría de los widgets nativos de Zabbix.
En la implementación por defecto, el método *promiseUpdate()* sigue un patrón general para recuperar datos del servidor.
Crea un nuevo objeto `Curl` con la URL y los parámetros de solicitud apropiados,
envía una solicitud `POST` utilizando el método *fetch()* con el objeto de datos construido por el método *getUpdateRequestData()*,
y procesa la respuesta (o una respuesta de error) con *processUpdateResponse(response)* o *processUpdateErrorResponse(error)* según corresponda.
Esta implementación es adecuada para la mayoría de los widgets, ya que normalmente recuperan datos en formato JSON y los gestionan de manera coherente.

```js
promiseUpdate() {
    const curl = new Curl('zabbix.php');

    curl.setArgument('action', `widget.${this._type}.view`);

    return fetch(curl.getUrl(), {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(this.getUpdateRequestData()),
        signal: this._update_abort_controller.signal
    })
        .then((response) => response.json())
        .then((response) => {
            if ('error' in response) {
                this.processUpdateErrorResponse(response.error);

                return;
            }

            this.processUpdateResponse(response);
        });
    }
```

[comment]: # ({/fa94eb63-c743ab46})

[comment]: # ({5d0423d9-5b7f40bf})
El método ***getUpdateRequestData()*** prepara los datos de la solicitud al servidor para actualizar el widget recopilando varias propiedades y sus valores correspondientes (identificadores de widget, configuraciones de filtro, rangos de tiempo, etc.) del estado y la configuración del widget, y construyendo un objeto de datos que representa la información necesaria que se enviará al servidor en la solicitud de actualización.
Este método se invoca solo como parte del método predeterminado *promiseUpdate()*, es decir, durante el proceso de actualización del widget.

Implementación por defecto:

```js
getUpdateRequestData() {
    return {
        templateid: this._dashboard.templateid ?? undefined,
        dashboardid: this._dashboard.dashboardid ?? undefined,
        widgetid: this._widgetid ?? undefined,
        name: this._name !== '' ? this._name : undefined,
        fields: Object.keys(this._fields).length > 0 ? this._fields : undefined,
        view_mode: this._view_mode,
        edit_mode: this._is_edit_mode ? 1 : 0,
        dynamic_hostid: this._dashboard.templateid !== null || this.supportsDynamicHosts()
            ? (this._dynamic_hostid ?? undefined)
            : undefined,
        ...this._contents_size
    };
}
```

[comment]: # ({/5d0423d9-5b7f40bf})

[comment]: # ({980ceee3-dd2550e1})
El método ***processUpdateResponse(response)*** gestiona la respuesta recibida del servidor tras la solicitud de actualización y, si el proceso de actualización ha sido exitoso y sin errores, limpia los datos del widget y muestra los nuevos contenidos con el método *setContents()*. Este método se invoca únicamente como parte del método por defecto *promiseUpdate()*, es decir, durante el proceso de actualización del widget.

Implementación por defecto:

```js
processUpdateResponse(response) {
    this._setHeaderName(response.name);

    this._updateMessages(response.messages);
    this._updateInfo(response.info);
    this._updateDebug(response.debug);

    this.setContents(response);
}
```

[comment]: # ({/980ceee3-dd2550e1})

[comment]: # ({d3ce9166-2abd3964})
El método ***processUpdateErrorResponse(error)*** gestiona la respuesta recibida del servidor después de la solicitud de actualización si la respuesta es un error, y muestra el/los mensaje(s) de error.
Este método se invoca solo como parte del método por defecto *promiseUpdate()*, es decir, durante el proceso de actualización del widget.

Implementación por defecto:

```js
processUpdateErrorResponse(error) {
    this._updateMessages(error.messages, error.title);
}
```

[comment]: # ({/d3ce9166-2abd3964})

[comment]: # ({31ffc8a7-8ea0452d})
El método ***setContents(response)*** muestra el contenido del widget si el proceso de actualización del widget se ha realizado correctamente y sin errores,
lo que puede incluir la manipulación de elementos DOM, la actualización de componentes de la interfaz de usuario, la aplicación de estilos o formatos, etc.
Este método se invoca solo como parte del método predeterminado *processUpdateResponse(response)*, es decir, durante el proceso de manejo de la respuesta recibida del servidor después de la solicitud de actualización.

Implementación por defecto:

```js
setContents(response) {
    this._body.innerHTML = response.body ?? '';
}
```

[comment]: # ({/31ffc8a7-8ea0452d})

[comment]: # ({7b8ba0e6-95f750ef})
##### Métodos de modificación de presentación

Los métodos de modificación de presentación del widget son responsables de modificar la apariencia del widget.

[comment]: # ({/7b8ba0e6-95f750ef})

[comment]: # ({5e245dd6-b5613994})
El método ***onResize()*** es responsable de ajustar los elementos visuales del widget para adaptarse al nuevo tamaño del widget,
lo que puede incluir reorganizar elementos, ajustar dimensiones de los elementos, truncar texto, implementar carga diferida para mejorar la capacidad de respuesta durante el cambio de tamaño, etc.
Este método se invoca cuando se cambia el tamaño del widget, por ejemplo, cuando el usuario cambia manualmente el tamaño del widget o cuando se cambia el tamaño de la ventana del navegador.

Ejemplo:

```js
onResize() {
    if (this.getState() === WIDGET_STATE_ACTIVE) {
        this._startUpdating();
    }
}
```

[comment]: # ({/5e245dd6-b5613994})

[comment]: # ({d22bedba-88b32413})
El método ***hasPadding()*** es responsable de aplicar un padding vertical de 8px en la parte inferior del widget cuando está configurado para [mostrar su encabezado](/manual/web_interface/frontend_sections/dashboards/widgets#common-parameters).
Este método se invoca cuando la página del dashboard se activa, es decir, cuando se convierte en la página mostrada en la interfaz de usuario.

Implementación por defecto:

```js
hasPadding() {
    return this.getViewMode() !== ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER;
}
```

Para algunos widgets es necesario utilizar todo el espacio disponible del widget para configurar, por ejemplo, un color de fondo personalizado.
A continuación se muestra un ejemplo de la implementación del método *hasPadding()* utilizado en el widget nativo de Zabbix [*Valor de la métrica*](/manual/web_interface/frontend_sections/dashboards/widgets/item_value).

```js
hasPadding() {
    return false;
}
```

[comment]: # ({/d22bedba-88b32413})
