[comment]: # aside:2

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

This page describes the components that can be used to create a widget presentation view.
The widget presentation view is the part of the widget that receives the data according to its [configuration](/configuration) and displays it on the dashboard in a container.

The presentation view consists of three parts:

-   [Widget action](#widget-action)
-   [Widget view](#widget-view)
-   [JavaScript](#javascript)

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

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

The widget action class (*WidgetView*) contains methods for operations with widgets in the presentation view mode.
The majority of widget actions use and/or extend the default controller class *CControllerDashboardWidgetView*.

The widget action class should be located in the *actions* directory and specified in the [*actions*](/devel/modules/file_structure/manifest#actions) parameter (*actions/widget.{id}.view/class*) in the *manifest.json* file.

**actions/WidgetView.php example (implemented in the Zabbix-native [*System information*](/manual/web_interface/frontend_sections/dashboards/widgets/system) widget)**

```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})
### Widget view

The widget view class (*CWidgetView*) is responsible for building the widget presentation view.

The widget view class should be located in the *views* directory.
If the file containing the widget view class has a different name than the default (*widget.view.php*), then it must be specified in the *manifest.json* file [*actions*](/devel/modules/file_structure/manifest#actions) parameter (*actions/widget.{id}.view/view*).

**views/widget.view.php example**

```php
<?php

/**
 * My custom widget view.
 *
 * @var CView $this
 * @var array $data
 */

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

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

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

The JavaScript class is responsible for determining widget behavior, such as updating widget data, resizing the widget, displaying widget elements, etc.

All JavaScript operations use and/or extend the base JavaScript class of all dashboard widgets - *CWidget*.
The *CWidget* class contains a set of methods with the default implementation for widget behavior.
Depending on widget complexity, these methods can be utilized as is or extended.

The *CWidget* class contains the following methods:

-   Methods that define widget lifecycle: *onInitialize()*, *onStart()*, *onActivate()*, *onDeactivate()*, *onDestroy()*, *onEdit()*.
-   Methods that handle updating and displaying widget data: *promiseUpdate()*, *getUpdateRequestData()*, *processUpdateResponse(response)*, *processUpdateErrorResponse(error)*, *setContents(response)*.
-   Methods that modify widget appearance: *onResize()*, *hasPadding()*.

The JavaScript class should be located in the *assets/js* directory and specified in the [*assets*](/devel/modules/file_structure/manifest#assets) (*assets/js*) parameter in the *manifest.json* file.

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

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

The widget lifecycle methods are invoked by the dashboard, and at different stages of the widget's lifecycle during its existence within the dashboard.

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

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

The ***onInitialize()*** method defines the initial state and/or values of the widget, without performing any HTML or data manipulation.
This method is invoked when a widget is created (a widget object is instantiated), typically by adding the widget to a dashboard page or loading the dashboard page.

Example:

```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})

The ***onStart()*** method defines the HTML structure of the widget, without performing any data manipulation.
This method is invoked before the first activation of the dashboard page, that is, before the dashboard and its widgets are fully displayed to the user.

Example:

```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})

The ***onActivate()*** method makes the widget active and interactive by enabling custom event listeners (for responding to user actions) and initiating the widget update cycle (for keeping its content up-to-date).
This method is invoked when the dashboard page is activated, that is, when it becomes fully displayed in the user interface.

Note that before the *onActivate()* method is invoked, the widget is in the inactive state (`WIDGET_STATE_INACTIVE`).
After successful invocation, the widget transitions to the active state (`WIDGET_STATE_ACTIVE`).
In the active state, the widget is responsive, listens to events, updates its content periodically, and can interact with other widgets.

Example:

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

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

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

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

The ***onDeactivate()*** method stops any activity and interactivity of the widget by deactivating custom event listeners and stopping the widget update cycle.
This method is invoked when the dashboard page is deactivated, that is, switched away or deleted, or when the widget is deleted from the dashboard page.

Note that before the *onDeactivate()* method is invoked, the widget is in the active state (`WIDGET_STATE_ACTIVE`).
After successful invocation, the widget transitions to the inactive state (`WIDGET_STATE_INACTIVE`).

Example:

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

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

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

The ***onDestroy()*** method performs cleanup tasks before the widget is deleted from the dashboard,
which can include closing a database connection that was established during widget initialization,
cleaning up temporary data to free up system memory and avoid resource leaks,
unregistering event listeners related to resize events or button clicks to prevent unnecessary event handling and memory leaks, etc.
This method is invoked when the widget or the dashboard page that contains it is deleted.

Note that before the *onDestroy()* method is invoked, a widget in an active state (`WIDGET_STATE_ACTIVE`) is always deactivated with the invocation of the *onDeactivate()* method.

Example:

```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})

The ***onEdit()*** method defines the appearance and behavior of the widget when the dashboard transitions into edit mode.
This method is invoked when the dashboard transitions into edit mode, typically when a user interacts with the widget's *Edit* button or the dashboard's *Edit dashboard* button.

Example:

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

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

[comment]: # ({1e54f2e8-87f28aae})
##### Update process methods

The widget update process methods are responsible for retrieving updated data from Zabbix server or any other data source and displaying it in the widget.

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

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

The ***promiseUpdate()*** method initiates the data update process by retrieving data, typically using web requests or API calls.
This method is invoked when a dashboard page is displayed and periodically after, until the dashboard page is switched to another dashboard page.

The following is an example of the default implementation of the *promiseUpdate()* method used by most Zabbix-native widgets.
In the default implementation, the *promiseUpdate()* method follows a general pattern for retrieving data from the server.
It creates a new `Curl` object with the appropriate URL and request parameters,
sends a `POST` request using the *fetch()* method with the data object constructed by the *getUpdateRequestData()* method,
and processes the response (or an error response) with the *processUpdateResponse(response)* or *processUpdateErrorResponse(error)* accordingly.
This implementation is suitable for most widgets as they typically retrieve data in a JSON format and handle it in a consistent manner.

```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})

The ***getUpdateRequestData()*** method prepares the server request data for updating the widget by gathering various properties and their corresponding values (widget identifiers, filter settings, time ranges, etc.) from the widget's state and configuration,
and constructing a data object that represents the necessary information to be sent to the server in the update request.
This method is invoked only as part of the default *promiseUpdate()* method, that is, during the widget update process.

Default implementation:

```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})

The ***processUpdateResponse(response)*** method handles the response received from the server after the update request,
and, if the update process has been successful and without errors, clears widget data and displays new contents with the *setContents()* method.
This method is invoked only as part of the default *promiseUpdate()* method, that is, during the widget update process.

Default implementation:

```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})

The ***processUpdateErrorResponse(error)*** method handles the response received from the server after the update request if the response is an error, and displays the error message/-s.
This method is invoked only as part of the default *promiseUpdate()* method, that is, during the widget update process.

Default implementation:

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

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

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

The ***setContents(response)*** method displays widget contents if the widget update process has been successful and without errors,
which can include manipulating DOM elements, updating UI components, applying styles or formatting, etc.
This method is invoked only as part of the default *processUpdateResponse(response)* method, that is, during the process of handling the response received from the server after the update request.

Default implementation:

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

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

[comment]: # ({7b8ba0e6-95f750ef})
##### Presentation modification methods

The widget presentation modification methods are responsible for modifying widget appearance.

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

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

The ***onResize()*** method is responsible for adjusting widget's visual elements to accommodate the new widget size,
which can include rearranging elements, adjusting element dimensions, text truncation, implementing lazy loading to improve responsiveness during resizing, etc.
This method is invoked when the widget is resized, for example, when the user manually resizes the widget or when the browser window is resized.

Example:

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

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

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

The ***hasPadding()*** method is responsible for applying an 8px vertical padding at the bottom of the widget when it is configured to [show its header](/manual/web_interface/frontend_sections/dashboards/widgets#common-parameters).
This method is invoked when the dashboard page is activated, that is, when it becomes the displayed page in the user interface.

Default implementation:

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

For some widgets it is necessary to use all of the available widget space to configure, for example, a custom background color.
The following is an example of the implementation of the *hasPadding()* method used in the Zabbix-native [*Item value*](/manual/web_interface/frontend_sections/dashboards/widgets/item_value) widget.

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

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