Введение
Привет! Меня зовут Павел и я Magento 2 бэкенд-разработчик. В прошлых частях саги о Magento 2 UI Components мы получили общие сведения о UI-компонентах, а также рассмотрели наиболее важные аспекты их конфигурации. Сегодня подробно коснемся некоторых вопросов модификации UI компонентов под свои нужды: изменение внешнего вида, поведения клиентской части, поведения серверной части и пр. Погнали!
Изменение внешнего вида
Начнем с наиболее простой части — изменение внешнего вида. У всех компонентов, имеющих какое либо визуальное представление, внешний вид задается шаблоном. Для примера возьмем шаблон компонента Input
:
<input class="admin__control-text" type="text"
data-bind="
event: {change: userChanges},
value: value,
hasFocus: focused,
valueUpdate: valueUpdate,
attr: {
name: inputName,
placeholder: placeholder,
'aria-describedby': noticeId,
id: uid,
disabled: disabled,
maxlength: 255
}"/>
Попробуем изменить его, для чего в нашем модуле создадим файл input.html
в /view/adminhtml/web/templates/form/element/
нашего модуля и поместим туда следующий текст:
<span>White Rabbit</span><!-- добавим элемент для наглядности -->
<input class="admin__control-text" type="text"
data-bind="
event: {change: userChanges},
value: value,
hasFocus: focused,
valueUpdate: valueUpdate,
attr: {
name: inputName,
placeholder: placeholder,
'aria-describedby': noticeId,
id: uid,
disabled: disabled,
maxlength: 255
}"/>
После чего в xml конфигурации, где объявлен наш компонент, укажем новый шаблон для компонента примерно таким образом:
<field name="white_rabbit" sortOrder="10" formElement="input">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
...
<item name="elementTmpl" xsi:type="string">RSHB_WhiteRabbit/form/element/input</item>
...
</item>
</argument>
</field>
Очистим кэш и посмотрим, как изменился внешний вид компонента:
Вместе с кастомизацией шаблона мы можем расширить функционал UI-компонента, например, использовать в шаблоне knockout конструкции, которые будут обращаться к кастомным методам JS-компонента.
Кастомизация JS-компонента
Перейдем к более интересному моменту, а именно — к управлению поведением компонента в браузере пользователя. Как мы помним из предыдущей статьи, одной из частей UI-компонента является JS-виджет, который обеспечивает интерактивность компонента и отвечает за его поведение. Многие простейшие компоненты, такие как рассмотренный ранее input, не имеют своей реализации JS-компонента, вместо этого используя общую для всех реализацию abstract (которую можно найти в <magento_root>/vendor/magento/module-ui/view/base/web/js/form/element/abstract.js
).
Поэтому, посмотрим на один из самых простых специализированных JS-компонентов — компонент Textarea:
define([
'./abstract'
], function (Abstract) {
'use strict';
return Abstract.extend({
defaults: {
cols: 15,
rows: 2,
elementTmpl: 'ui/form/element/textarea'
}
});
});
Как мы видим, данный компонент расширяет реализацию abstract и вносит минимум изменений, а именно, задает размеры поля по умолчанию и его шаблон (тот самый, который мы обсуждали ранее).
У нас есть два пути модификации JS-компонента.
В первом случае, если мы хотим кардинально изменить поведение компонента, то можно написать новый JS-компонент, и указать его в xml конфигурации UI-компонента вместо использующегося по умолчанию. Для этого в нашем модуле создадим файл textarea.js
по адресу view/adminhtml/web/js/form/element/
, поместим туда модифицированный JS-компонент, а затем укажем его в xml конфигурации следующим образом:
<field name="white_rabbit" sortOrder="10" formElement="textarea">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
...
<item name="component" xsi:type="string">RSHB_WhiteRabbit/js/form/element/textarea</item>
...
</item>
</argument>
</field>
Стоит заметить, что если нет нужды переписывать весь компонент, то достаточно его расширить, если вносимые изменения незначительны:
define([
'Magento_Ui/js/form/element/textarea'
], function (MagentoTextarea) {
'use strict';
return MagentoTextarea.extend({
sayHello: function () {
alert('Hello!');
return this;
},
});
});
Так мы добавили нашему компоненту функцию sayHello
которую можно использовать по своему смотрению.
Альтернативой полной замене JS-компонента может служить миксин к базовому JS-компоненту. В отличие от описанного ранее, данный метод поменяет поведение всех экземпляров компонента в системе, это следует держать в голове при выборе способа модификации JS-компонента.
Чтобы изменить поведение компонента при помощи миксина, необходимо создать миксин и указать его в requirejs. Для примера, применим миксин к базовому компоненту form, для чего поместим файл requirejs-config.js
в /view/base/
нашего модуля:
var config = {
'config': {
'mixins': {
'Magento_Ui/js/form/form': {
'RSHB_WhiteRabbit/form-hook': true
}
}
}
};
И далее разместим в /view/base/web/form-hook.js
нашего модуля код мисксина:
define([], function () {
'use strict';
return function (Form) {
return Form.extend({
initialize: function () {
this._super();
console.log('The mixin from White Rabbit!');
}
});
}
});
Таким образом, мы дополнили функцию инициализации компонента Form нашим кодом, и теперь каждый раз при выводе компонента Form в консоли будет выводиться строка “The mixin from White Rabbit!”.
PHP-классы
Теперь рассмотрим модификацию серверной части UI-компонента. Для этой цели служат кастомные классы компонентов и PHP-модификаторы (о последних поговорим позже).
Представим ситуацию, когда в базе у нас лежит значение int, которое определяет, включен некий параметр, или нет (значения 1 или 0, аналогично булевым true-false). При использовании стандартного класса column в гриде, мы увидим в колонке значения 1 или 0, в зависимости от значения в базе. Если мы хотим выводить эти значения как “Включено” или “Выключено”, нам необходимо подменить класс компонента, например:
<?php
...
class OnOff extends \Magento\Ui\Component\Listing\Columns\Column
{
/**
* OnOff constructor
*
* @param ContextInterface $context
* @param UiComponentFactory $uiComponentFactory
* @param array $components
* @param array $data
*/
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
array $components = [],
array $data = []
) {
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as $id => $item) {
$whiteRabbit = $item['white_rabbit'];//Находим поле с нужным именем whi
$item['white_rabbit'] = (bool)$whiteRabbit ? 'On' : 'Off';
$dataSource['data']['items'][$id] = $item;
}
}
return $dataSource;
}
}
Как мы видим, кастомный класс компонента меняет значения 1 и 0 на “Включено” и “Выключено”.
Стоит помнить, что при изменении значения в колонке нужно также изменять значения в фильтрах (если для колонки предусмотрен фильтр). Для этого следует указать Source класс со значениями, которые соответствуют значениям “Включено” и “Выключено” в нашем примере. Код Source класса:
<?php
...
class OnOff implements \Magento\Framework\Option\ArrayInterface
{
/**
* @return array
*/
public function toOptionArray(): array
{
return [['value' => 1, 'label' => __('On')], ['value' => 0, 'label' => __('Off')]];
}
/**
* @return array
*/
public function toArray(): array
{
return [0 => __('Off'), 1 => __('On')];
}
}
Кстати, в М2 есть ряд предопределенных Source классов для наиболее распространенных задач, например, значения Yes/No, перечень категорий товаров и пр.
PHP-модификаторы
Еще одним любопытным способом изменить или дополнить поведение серверной части UI-компонента (а именно, его дата-провайдера) — PHP модификаторы. Они применяются в случаях, когда статическое объявление PHP класса в XML конфигурации по каким-то причинам не подходит, или может вызвать избыточное дублирование кода.
PHP модификатор должен реализовывать Magento\Ui\DataProvider\Modifier\ModifierInterface
и должен содержать два метода: modifyData()
и modifyMeta()
. Посмотрим пример такого модификатора:
<?php
...
class WhiteRabbitModifier implements Magento\Ui\DataProvider\Modifier\ModifierInterface
{
/**
* {@inheritdoc}
*/
public function modifyMeta(array $meta)
{
$meta['test_fieldset_name'] = [
'arguments' => [
'data' => [
'config' => [
'label' => __('Label For Fieldset'),
'sortOrder' => 50,
'collapsible' => true
]
]
],
'children' => [
'test_field_name' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => 'select',
'componentType' => 'field',
'options' => [
['value' => 'test_value_1', 'label' => 'Test Value 1'],
['value' => 'test_value_2', 'label' => 'Test Value 2'],
['value' => 'test_value_3', 'label' => 'Test Value 3'],
],
'visible' => 1,
'required' => 1,
'label' => __('Label For Element')
]
]
]
]
]
];
return $meta;
}
/**
* {@inheritdoc}
*/
public function modifyData(array $data)
{
return $data;
}
}
Как мы видим, при помощи модификатора можно изменить данные и мета-данные UI-компонента.
Для того, чтобы модификатор заработал, его нужно добавить в пул модификаторов дата-провайдера через di.xml таким способом:
<virtualType name="RSHB\WhiteRabbit\DataProvider\Modifier\WhiteRabbitPool" type="Magento\Ui\DataProvider\Modifier\Pool">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="modifier_name" xsi:type="array">
<item name="class" xsi:type="string">RSHB\WhiteRabbit\Modifier\WhiteRabbitModifier</item>
<item name="sortOrder" xsi:type="number">10</item>
</item>
</argument>
</arguments>
</virtualType>
<type name="RSHB\WhiteRabbit\Ui\DataProvider\WhiteRabbitDataProvider">
<arguments>
<argument name="pool" xsi:type="object">RSHB\WhiteRabbit\DataProvider\Modifier\WhiteRabbitPool</argument>
</arguments>
</type>
Заключение
Мы рассмотрели основные аспекты модификации UI-компонентов под свои нужды. На этом цикл статей об UI-компонентах я считаю закрытым, возникающие вопросы по нюансам работы можно задавать в комментариям к соответствующим статьям.
Пишите код с удовольствием! До встречи.