Widget presentation is part of the widget module that receives the data according to the configuration and displays it on the dashboard in a container. Widget presentation container can be positioned and resized.
The widget view consists of two optional parts:
The majority of widget actions use and/or extend the default controller class CControllerDashboardWidgetView. This class contains methods for operations with widgets in the view mode.
The class source file must be located in the actions directory and must be specified as an action class in the manifest.json in the actions/widget.{id}.view/class section.
For example, this is how extending the default class is implemented in the Zabbix-native widget System information:
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()
]
]));
}
}
Widget presentation view is being built by class CWidgetView.
The widget's view file should be located in the views directory. If the file has a default name widget.view.php, there is no need to register it in the manifest.json. If the file has a different name, specify it in the actions/widget.{id}.view/view section of manifest.json.
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:
The JavaScript class should be located in the assets/js directory and specified in the assets (assets/js) parameter in the manifest.json file.
The widget lifecycle methods are invoked by the dashboard, and at different stages of the widget's lifecycle during its existence within the dashboard.
The _init() 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:
_init() {
super._init();
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;
}
The _registerEvents() 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:
_registerEvents() {
super._registerEvents();
this._events.resize = () => {
const padding = 25;
const header_height = this._view_mode == ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER
? 0
: this._content_header.offsetHeight;
this._target.style.setProperty(
'--content-height',
`${this._cell_height * this._pos.height - padding * 2 - header_height}px`
);
}
}
The _doActivate() 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 _doActivate() 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:
The _doDeactivate() 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 _doDeactivate() 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:
The _doDestroy() 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 _doDestroy() method is invoked, a widget in an active state (WIDGET_STATE_ACTIVE
) is always deactivated with the invocation of the _doDeactivate() method.
Example:
_doDestroy() {
super._doDestroy();
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);
}
}
The setEditMode() method defines the appearance and behavior of the widget when the dashboard transitions into editing mode. This method is invoked when the dashboard transitions into editing mode, typically when a user interacts with the widget's Edit button or the dashboard's Edit dashboard button.
Example:
setEditMode() {
if (this._has_contents) {
this._deactivateContentsEvents();
this._removeTree();
}
super.setEditMode();
if (this._has_contents && this._state === WIDGET_STATE_ACTIVE) {
this._makeTree();
this._activateTree();
this._activateContentsEvents();
}
}
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.
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.
_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);
});
}
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:
_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._content_size
};
}
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:
_processUpdateResponse(response) {
this._setContents({
name: response.name,
body: response.body,
messages: response.messages,
info: response.info,
debug: response.debug
});
}
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:
_processUpdateErrorResponse(error) {
this._setErrorContents({error});
}
_setErrorContents({error}) {
const message_box = makeMessageBox('bad', error.messages, error.title)[0];
this._content_body.innerHTML = '';
this._content_body.appendChild(message_box);
this._removeInfoButtons();
}
The _setContents() 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:
_setContents({name, body, messages, info, debug}) {
this._setHeaderName(name);
this._content_body.innerHTML = '';
if (messages !== undefined) {
const message_box = makeMessageBox('bad', messages)[0];
this._content_body.appendChild(message_box);
}
if (body !== undefined) {
this._content_body.insertAdjacentHTML('beforeend', body);
}
if (debug !== undefined) {
this._content_body.insertAdjacentHTML('beforeend', debug);
}
this._removeInfoButtons();
if (info !== undefined) {
this._addInfoButtons(info);
}
}
The widget presentation modification methods are responsible for modifying widget appearance.
The resize() 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:
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. This method is invoked when the dashboard page is activated, that is, when it becomes the displayed page in the user interface.
Default implementation:
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 widget.