Widgets
Widgets are self-contained blocks of functionality that solve different tasks. Widgets always have a user interface and a backend controller (the widget class) that prepares the widget data and handles AJAX requests generated by the widget user interface.
Generic Widgets
Widgets are the backend equivalent of frontend Components. They are similar because they are modular bundles of functionality, supply partials and are named using aliases. The key difference is that backend widgets use YAML markup for their configuration and bind themselves to Backend pages.
Widget classes reside inside the widgets directory of the plugin directory. The directory name matches the name of the widget class written in lowercase. Widgets can supply assets and partials. An example widget directory structure looks like this:
plugins/
`-- acme/
`-- blog/
`-- widgets/
|-- form/
| |-- partials/
| | `-- _form.htm # Widget partial file
| `-- assets/
| |-- js/
| | `-- form.js # Widget JavaScript file
| `-- css/
| `-- form.css # Widget stylesheet
`-- Form.php # Widget class
Widget definition
The generic widget classes must extend the Backend\Classes\WidgetBase
class. As any other plugin class, generic widget controllers should belong to the plugin namespace. Example widget controller class definition:
<?php namespace Backend\Widgets;
use Backend\Classes\WidgetBase;
class Lists extends WidgetBase
{
/**
* @var string A unique alias to identify this widget.
*/
protected $defaultAlias = 'list';
// ...
}
The widget class must contain a render()
method for producing the widget markup by rendering a widget partial. Example:
public function render()
{
return $this->makePartial('list');
}
To pass variables to partials you can either add them to the $vars
property.
public function render()
{
$this->vars['var'] = 'value';
return $this->makePartial('list');
}
Alternatively you may pass the variables to the second parameter of the makePartial() method:
public function render()
{
return $this->makePartial('list', ['var' => 'value']);
}
Injecting page assets with widgets
Widgets can inject assets (CSS and JavaScript files) to pages or layouts they're attached to by using the controller's addCss and addJs methods to add assets to the CMS controllers. This should be done in the widget's loadAssets()
method. See the Asset Compiler docs for more information.
protected function loadAssets()
{
$this->addCss('css/form.css');
$this->addJs('css/form.js', 'Acme.Test');
}
AJAX handlers
Widgets implement the same AJAX approach as the backend controllers. The AJAX handlers are public methods of the widget class with names starting with the on prefix. The only difference between the widget AJAX handlers and backend controller's AJAX handlers is that you should use the widget's getEventHandler
method to return the widget's handler name when you refer to it in the widget partials.
<a
href="javascript:;"
data-request="<?= $this->getEventHandler('onPaginate') ?>"
title="Next page">Next</a>
When called from a widget class or partial the AJAX handler will target itself. For example, if the widget uses the alias of mywidget the handler will be targeted with mywidget::onName
. The above would output the following attribute value:
data-request="mywidget::onPaginate"
Binding widgets to controllers
A widget should be bound to a backend controller before you can start using it in a backend page or partial. Use the widget's bindToController
method for binding it to a controller. The best place to initialize a widget is the controller's constructor. Example:
public function __construct()
{
parent::__construct();
$myWidget = new MyWidgetClass($this);
$myWidget->alias = 'myWidget';
$myWidget->bindToController();
}
After binding the widget you can access it in the controller's view or partial by its alias:
<?= $this->widget->myWidget->render() ?>
Form Widgets
With form widgets you can add new control types to the backend forms. They provide features that are common to supplying data for models. Form widgets must be registered in the Plugin registration file.
Form Widget classes reside inside the formwidgets directory of the plugin directory. The directory name matches the name of the widget class written in lowercase. Widgets can supply assets and partials. An example form widget directory structure looks like this:
formwidgets/
|-- form/
| |-- partials/
| | `-- _form.php # Widget partial file
| `-- assets/
| |-- js/
| | `-- form.js # Widget JavaScript file
| `-- css/
| `-- form.css # Widget StyleSheet file
`-- Form.php # Widget class
Form widget definition
The form widget classes must extend the Backend\Classes\FormWidgetBase
class. As any other plugin class, generic widget controllers should belong to the plugin namespace. A registered widget can be used in the backend form field definition file. Example form widget class definition:
namespace Backend\Widgets;
use Backend\Classes\FormWidgetBase;
class CodeEditor extends FormWidgetBase
{
/**
* @var string A unique alias to identify this widget.
*/
protected $defaultAlias = 'codeeditor';
public function render() {}
}
Form widget properties
Form widgets may have properties that can be set using the form field configuration. Simply define the configurable properties on the class and then call the fillFromConfig
method to populate them inside the init
method definition.
class DatePicker extends FormWidgetBase
{
//
// Configurable properties
//
/**
* @var bool Display mode: datetime, date, time.
*/
public $mode = 'datetime';
/**
* @var string the minimum/earliest date that can be selected.
* eg: 2000-01-01
*/
public $minDate = null;
/**
* @var string the maximum/latest date that can be selected.
* eg: 2020-12-31
*/
public $maxDate = null;
//
// Object properties
//
/**
* {@inheritDoc}
*/
protected $defaultAlias = 'datepicker';
/**
* {@inheritDoc}
*/
public function init()
{
$this->fillFromConfig([
'mode',
'minDate',
'maxDate',
]);
}
// ...
}
The property values then become available to set from the form field definition when using the widget.
born_at:
label: Date of Birth
type: datepicker
mode: date
minDate: 1984-04-12
maxDate: 2014-04-23
Form widget registration
Plugins should register form widgets by overriding the registerFormWidgets
method inside the Plugin registration class. The method returns an array containing the widget class in the keys and widget short code as the value. Example:
public function registerFormWidgets()
{
return [
'Backend\FormWidgets\CodeEditor' => 'codeeditor',
'Backend\FormWidgets\RichEditor' => 'richeditor'
];
}
The short code is optional and can be used when referencing the widget in the Form field definitions, it should be a unique value to avoid conflicts with other form fields.
Loading form data
The main purpose of the form widget is to interact with your model, which means in most cases loading and saving the value via the database. When a form widget renders, it will request its stored value using the getLoadValue
method. The getId
and getFieldName
methods will return a unique identifier and name for a HTML element used in the form. These values are often passed to the widget partial at render time.
public function render()
{
$this->vars['id'] = $this->getId();
$this->vars['name'] = $this->getFieldName();
$this->vars['value'] = $this->getLoadValue();
return $this->makePartial('myformwidget');
}
At a basic level the form widget can send the user input value back using an input element. From the above example, inside the myformwidget partial the element can be rendered using the prepared variables.
<input id="<?= $id ?>" name="<?= $name ?>" value="<?= e($value) ?>" />
Saving form data
When the time comes to take the user input and store it in the database, the form widget will call the getSaveValue
internally to request the value. To modify this behavior simply override the method in your form widget class.
public function getSaveValue($value)
{
return $value;
}
In some cases you intentionally don't want any value to be given, for example, a form widget that displays information without saving anything. Return the special constant called FormField::NO_SAVE_DATA
derived from the Backend\Classes\FormField
class to have the value ignored.
public function getSaveValue($value)
{
return \Backend\Classes\FormField::NO_SAVE_DATA;
}
Report Widgets
Report widgets can be used on the backend dashboard and in other backend report containers. Report widgets must be registered in the Plugin registration file.
You can easily scaffold a report widget using the
create:reportwidget
command. See scaffolding commands for more information.
Report widget definition
The report widget classes should extend the Backend\Classes\ReportWidgetBase
class. As any other plugin class, generic widget controllers should belong to the plugin namespace. The class should override the render
method in order to render the widget itself. Similarly to all backend widgets, report widgets use partials and a special directory layout. Example directory layout:
plugins/
`-- winter/ # Author name
`-- googleanalytics/ # Plugin name
`-- reportwidgets/ # Report widgets directory
|-- trafficsources/ # Widget files directory
| `-- partials/
| `-- _widget.php
`-- TrafficSources.php # Widget class file
Example report widget class definition:
namespace Winter\GoogleAnalytics\ReportWidgets;
use Backend\Classes\ReportWidgetBase;
class TrafficSources extends ReportWidgetBase
{
public function render()
{
return $this->makePartial('widget');
}
}
The widget partial could contain any HTML markup you want to display in the widget. The markup should be wrapped into the DIV element with the report-widget class. Using H3 element to output the widget header is preferable. Example widget partial:
<div class="report-widget">
<h3>Traffic sources</h3>
<div
class="control-chart"
data-control="chart-pie"
data-size="200"
data-center-text="180">
<ul>
<li>Direct <span>1000</span></li>
<li>Social networks <span>800</span></li>
</ul>
</div>
</div>
Inside report widgets you can use any charts or indicators, lists or any other markup you wish. Remember that the report widgets extend the generic backend widgets and you can use any widget functionality in your report widgets. The next example shows a list report widget markup.
<div class="report-widget">
<h3>Top pages</h3>
<div class="table-container">
<table class="table data" data-provides="rowlink">
<thead>
<tr>
<th><span>Page URL</span></th>
<th><span>Pageviews</span></th>
<th><span>% Pageviews</span></th>
</tr>
</thead>
<tbody>
<tr>
<td>/</td>
<td>90</td>
<td>
<div class="progress">
<div class="bar" style="90%"></div>
<a href="/">90%</a>
</div>
</td>
</tr>
<tr>
<td>/docs</td>
<td>10</td>
<td>
<div class="progress">
<div class="bar" style="10%"></div>
<a href="/docs">10%</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
NOTE: Report widgets are loaded through an AJAX request on accessing the page - they are not available immediately on the page. This means that inline
<script>
tags will not work. See Updating partials for more information on handling JavaScript in partials after an AJAX request.
Report widget properties
Report widgets may have properties that users can manage with the Inspector:
The properties should be defined in the defineProperties
method of the widget class. The properties are described in the components article. Example:
public function defineProperties()
{
return [
'title' => [
'title' => 'Widget title',
'default' => 'Top Pages',
'type' => 'string',
'validationPattern' => '^.+$',
'validationMessage' => 'The Widget Title is required.'
],
'days' => [
'title' => 'Number of days to display data for',
'default' => '7',
'type' => 'string',
'validationPattern' => '^[0-9]+$'
]
];
}
Report widget registration
Plugins can register report widgets by overriding the registerReportWidgets
method inside the Plugin registration class. The method should return an array containing the widget classes in the keys and widget configuration (label, context, and required permissions) in the values. Example:
public function registerReportWidgets()
{
return [
'Winter\GoogleAnalytics\ReportWidgets\TrafficOverview' => [
'label' => 'Google Analytics traffic overview',
'context' => 'dashboard',
'permissions' => [
'winter.googleanalytics.widgets.traffic_overview',
],
],
'Winter\GoogleAnalytics\ReportWidgets\TrafficSources' => [
'label' => 'Google Analytics traffic sources',
'context' => 'dashboard',
'permissions' => [
'winter.googleanaltyics.widgets.traffic_sources',
],
]
];
}
The label
element defines the widget name for the Add Widget popup window. The context
element defines the context where the widget could be used. Winter's report widget system allows to host the report container on any page, and the container context name is unique. The widget container on the Dashboard page uses the dashboard
context.