Выход версии 2.3.0 приблизил использование PWA на фронте Magento-приложений на расстояние вытянутой руки. И если для фронта видны какие-то подвижки в применяемых технологиях, то с админкой всё гораздо стабильнее — старый добрый лабиринт из различных типов файлов, которые нужно поредактировать, чтобы на UI проявилось что-то полезное, усовершенствовать не планируется. В этой статье я описываю создание собственного рендерера для колонки грида в админке — вещи довольно несложной и, в то же самое время, довольно полезной при правильном применении. Например, рендерер для формирования в гриде заказов ссылки на карточку клиента, оформившего заказ:
Базовые компоненты рендерера
"Родной" рендерер для ссылок в Magento состоит из двух файлов:
- ./module-ui/view/base/web/js/grid/columns/link.js — код компонента;
- ./module-ui/view/base/web/templates/grid/cells/link.html — UI-шаблон компонента (knockout);
В процессе обработки шаблона используются функции, предоставляемые кодом (объект $col
). Входными данными для обработки являются также текущая строка грида (объект $row
):
<div class="data-grid-cell-content"
if="!$col.isLink($row())"
text="$col.getLabel($row())"/>
<a class="action-menu-item"
if="$col.isLink($row())"
text="$col.getLabel($row())"
attr="href: $col.getLink($row())"/>
Данные для грида загружаются через провайдер данных. Типовой запрос примерно такой: "http://.../admin/mui/index/render/?namespace=sales_order_grid...". Структуру данных можно увидеть через панель инструментов разработчика в браузере. Для грида заказов она примерно такая:
{
"items": [
{
"id_field_name": "entity_id",
"entity_id": "1",
"status": "pending",
"store_id": "Main Website<br/> Main Website Store<br/> Default Store View<br/>",
"store_name": "Main WebsitenMain Website Storen",
"customer_id": "1",
"base_grand_total": "RUB34.68",
"base_total_paid": null,
"grand_total": "RUB34.68",
"total_paid": null,
"increment_id": "000000001",
"base_currency_code": "RUB",
"order_currency_code": "RUB",
"shipping_name": "Alex Gusev",
"billing_name": "Alex Gusev",
"created_at": "2018-12-22 19:35:19",
"updated_at": "2018-12-22 19:35:20",
"billing_address": "Street,Riga,Ru012bga,1010",
"shipping_address": "Street,Riga,Ru012bga,1010",
"shipping_information": "Flat Rate - Fixed",
"customer_email": "alex@flancer64.com",
"customer_group": "1",
"subtotal": "RUB24.68",
"shipping_and_handling": "RUB10.00",
"customer_name": "Alex Gusev",
"payment_method": "checkmo",
"total_refunded": "RUB0.00",
"signifyd_guarantee_status": null,
"orig_data": null,
"actions": {
"view": {
"href": "http://sample.local.flancer64.com/admin/sales/order/view/order_id/1/",
"label": "View"
}
}
}
],
"totalRecords": 1
}
Собственный рендерер
Таким образом, для создания собственного рендерера нам нужно задать UI-компонент, состоящий из двух файлов:
- JS-код компонента;
- Knockout-шаблон компонента;
Моей текущей задачей является создание рендерера, который бы выводил в ячейку грида заказов ссылку на клиента, оформившего заказ. Для формирования ссылки на клиента мне нужно использовать идентификатор соответствующего клиента — customer_id
. Можно написать свой собственный шаблон для рендеринга, но в данном случае меня вполне устраивает имеющийся шаблон (./module-ui/view/base/web/templates/grid/cells/link.html
). Достаточно переписать JS-код, который бы возвращал нужный результат при вызове функций $col.getLink($row())
и $col.isLink($row())
.
Я разделил свой код на две части. Файл base.js содержит базовую логику для формирования ссылки, используемой в шаблоне, а файл customer_name.js позволяет настраивать базовую логику формирования ссылки в соответствии с задачами конкретного столбца.
Базовый функционал
В качестве базы я беру существующий UI-компонент column
:
define([
"Magento_Ui/js/grid/columns/column",
"mageUtils"
], function (Column, utils) {
...
}
и (пере)определяю его атрибуты, указывая, что для рендеринга используется шаблон ui/grid/cells/link
(из модуля Magento_Ui
):
return Column.extend({
defaults: {
/**
* Replace idAttrName & route in children.
*/
/* name of the identification attribute */
idAttrName: "customer_id",
/* route part to the page */
route: "/customer/index/edit/id/",
bodyTmpl: "ui/grid/cells/link"
}
});
а затем (пере)определяю методы, используемые в шаблоне.
isLink
(ссылку можно сформировать, если данные record
содержат атрибут с именем, хранящимся в this.idAttrName
):
isLink: function (record) {
const result = !!utils.nested(record, this.idAttrName);
return result;
}
getLink
:
getLink: function (record) {
const id = utils.nested(record, this.idAttrName);
const result = ROOT_URL + this.route + id;
return result;
}
Ссылка на карточку клиента
В файле customer_name.js
базовый функционал переопределяется таким образом, чтобы формировалась ссылка на карточку клиента "http://.../admin/customer/index/edit/id/..." на основании идентификатора клиента customer_id
:
define([
"Flancer32_GridLink/js/grid/column/link/base"
], function (Column) {
"use strict";
return Column.extend({
defaults: {
idAttrName: "customer_id",
route: "/customer/index/edit/id/"
}
});
});
Подключение рендерера
Кастомный рендерер подключается к гриду в файле с определением соответствующего UI-компонента. В нашем случае это ./module-sales/view/adminhtml/ui_component/sales_order_grid.xml
. В собственном модуле создается файл ./view/adminhtml/ui_component/sales_order_grid.xml
в котором переопределяем рендерер для соответствующего столбца:
<listing ...>
<columns name="sales_order_columns">
<column name="customer_name"
component="Vendor_Module/js/grid/column/link/customer_name">
<settings>
<visible>true</visible>
</settings>
</column>
</columns>
</listing>
Опция settings/visible
нужна для того, чтобы колонка "customer_name" была видима в гриде (по-умолчанию она не видна).
Порядок загрузки
При сборке воедино всех xml-дескрипторов различных частей приложения в Magento (в том числе и описание UI-компонентов) важен порядок обработки дескрипторов, относящихся к одним и тем же компонентам, но находящимся в разных модулях. В нашем случае это ./view/adminhtml/ui_component/sales_order_grid.xml
. Если платформа сначала обработает дескриптор из нашего модуля, а потом из sales-модуля, то при слиянии дескрипторов конфигурация sales-модуля заместит нашу конфигурацию в тех местах, где определяются одни и те же атрибуты (так, параметр settings/visible
будет равен "false"), хотя рендерер все равно будет использоваться наш (sales-модуль не определяет рендерер для ячейки "Customer Name").
Порядок загрузки прописывается в ./etc/module.xml
:
<config ...>
<module name="Vendor_Module" setup_version="0.1.0">
<sequence>
<module name="Magento_Sales"/>
</sequence>
</module>
</config>
В этом случае наш модуль будет загружаться после модуля "Magento_Sales" и наши настройки, в случае совпадения с настройками в sales-модуле, заместят настройки sales-модуля.
Резюме
Предоставляемый Magento платформой набор рендереров довольно базовый (например, не нашёл рендерера для целых чисел с выравниванием по правому краю), но создание собственных рендереров может оживить стандартный вид Magento-грида в админке.
Код данной публикации оформлен в виде модуля "mage2_ext_grid_column_renderer".
Автор: flancer