Немного теории
Есть ряд задач, которые решаются через кастомизацию на css/js. Чаще всего это добавление новых элементов на страницу, но иногда и модификация стандартных возможностей платформы.
Такие кастомизации добавляются на события загрузки страницы (onProlog, onEpilog) посредством использования Bitrix\Main\Page\Asset.
addJs, addCss - принимают аргументом путь, относительно корня сайта, к подключаемому файлу.
addString - принимает аргументом строку, которая содержит произвольный скрипт. Такой подход используется чаще, так как метод позволяет вставить js/css в несколько фиксированных мест страницы, в отличие от addJs/addCss.
Доступные места описаны в классе Bitrix\Main\Page\AssetLocation в виде констант:
- до/после css;
- после подключения kernel js;
- после подключения js, если быть точнее, после подключения шаблона страницы;,
- в конце страницы.
Кастомизировать страницы посредством js можно произвольными скриптами или через JS-расширения.
Если требуется модифицировать что-то небольшое и настолько специфическое, что вы не представляете, как скрипт мог бы быть переиспользован кем-то, то нет смысла создавать js-расширение для этого.
Расширения нужны для всего остального. Они могут находиться в bitrix/js/<module>/<extension> или bitrix/modules/js/<module>/<extension>, но это устаревший подход или применимый для модулей размещаемых в Маркете. Правильно - в local/js/<vendor>/<module>/<extension>.
Состав расширения:
- src - исходные файлы;
- dist - бандлы для браузера;
- bundle.config.js - конфигурационный файл для сборщика;
- config.php - конфигурационный файл расширения.
Если установлен @bitrix/cli, то структуру расширения можно создать, выполнив команду bitrix create.
Однажды добавленный JS будет подключен на всех страницах. Поэтому само расширение и запуск кода из расширения лучше писать отдельно. Таким образом, JS-расширение будет всегда существовать и только лишь предоставлять доступ к своим методам, но фактически ничего не делать.
Подключить JS-расширение можно в двух местах, в зависимости от того, кто будет его использовать -
php:
- \Bitrix\Main\UI\Extension::load(‘<extension>’).
- CJSCore::Init(‘<extension>’).
js:
- import {class} from '<extension>’.
JS-расширение не обязательно должно содержать в себе полный описанный набор файлов и папок. Это может быть всего один файл, зарегистрированный как расширение. Регистрировать лучше всего в файле модуля include.php, так регистрация будет находиться в пределах вашего модуля и также данный файл автоматически будет вызван при подгрузке модуля, следовательно расширение станет доступно везде, где подключен сам модуль.
- CJSCore::RegisterExt.
- \Bitrix\Main\UI\Extension::registerAssets.
Если вы будете дорабатывать портал, используя js-скрипт, то он должен располагаться внутри BX.ready. Это позволит запуститься вашему коду, только когда DOM-структура будет полностью загружена и сформирована.
Следует учитывать, что ошибки при исполнении скрипта могут привести к полной неработоспособности всего JS на странице.
Кроме скриптов, исполняемых сразу при открытии страницы, может потребоваться выполнить что-то в определенный момент времени. Для этого во фреймворке реализованы js-события.
Чтобы подписаться, нужно использовать BX.addCustomEvent. В документации описано, как получить список доступных событий и как подписаться на существующее событие.
(function () {
BX.ready(function () {
const sendRemoteBatch = BX.namespace('module.example.sendRemoteBatch.Contact');
sendRemoteBatch.init = function () {
BX.addCustomEvent('Grid::ready', BX.proxy(this.insertAction, this));
BX.addCustomEvent('Grid::updated', BX.proxy(this.insertAction, this));
}
sendRemoteBatch.insertAction = function (grid) {
if (grid.getContainerId() !== 'CRM_CONTACT_LIST_V12') {
return;
}
const actionDropdown = grid.getActionsPanel().getDropdowns()[0];
if (!actionDropdown) {
return;
}
let items = BX.parseJSON(BX.data(actionDropdown, 'items'));
if (!BX.type.isArray(items)) {
return;
}
if (items.find(i => i.VALUE === 'send_to_remote_system')) {
return;
}
items.push({
NAME: 'Send to Remote System',
VALUE: 'send_to_remote_system',
ONCHANGE: [
{
ACTION: 'CALLBACK',
DATA: [
{JS: 'BX.module.example.sendRemoteBatch.Contact.process()'}
]
}
]
});
actionDropdown.dataset.items = JSON.stringify(items);
}
Например, подписавшись на события Grid::ready и Grid::updated, можно управлять групповыми действиями грида. В параметрах этих событий можно найти панель ActionsPanel и все ее элементы. На картинке пример добавления своего действия к списку контактов Send to Remote System.
Помимо подписки на существующие события, можно и создавать свои через BX.onCustomEvent.
Для примера выше требуется реализовать функциональность отправки контакта во внешнюю систему. Для этого потребуется написать ajax-контроллер. В документации описано, как создать свой контроллер и как к нему обратиться из js-скрипта. Контроллеры нужны везде, где необходимо взаимодействие с бэкендом.
Мы уже сказали об использовании Asset::addString, как о способе добавить свои скрипты на страницу. Но помимо скриптов, вам может понадобиться вывести компонент или просто верстку чего-то. Это тоже можно реализовать через addString, если использовать буферизацию.
Работа с буфером вывода начинается с функции ob_start(). После этого все, что попадает в STDOUT (поток вывода), будет сохранено в буфере. Если же нам надо обработать вывод, то потребуется функция ob_get_contents(). Сохранив данные, можно очистить и отключить буфер - для этого воспользуемся функцией ob_clean().
Пример:
ob_start();
$APPLICATION->IncludeComponent(
'module:component.name',
'.default',
array(
'TITLE' => 'Some title parameter',
)
);
$content = ob_get_contents();
ob_clean();
Asset::getInstance()->addString($content);
При кастомизации системы можно не только писать свои скрипты, но и использовать JS-расширения платформы. Например, можно реализовать свое всплывающее окно (pop-up). Для этого может пригодиться диалоговое окно или оконная библиотека. Внутри такого окна можно расположить что угодно. К примеру, это может быть форма с полями ввода или статический текст.
Практика
Типовые задачи, решаемые js/css кастомизацией
Изменение дизайна портала на корпоративные цвета, добавление новых элементов управления в различные места, не имеющие подходящих точек расширения (кнопок, ссылок, ...).
Пример: изменение цвета кнопки
Для начала рассмотрим очень простой пример: необходимо изменить цвет кнопки «Создать» на красный.
Аналогичным способом вы сможете решить различные задачи, связанные с изменением визуальной составляющей шаблона портала, не трогая ядро продукта.
Наш пример проще всего решить с помощью переопределения CSS стилей. Для этого:
- Определим элемент HTML для модификации.
- Создадим стиль и присвоим его нужному элементу по id или классу стилей.
- Зарегистрируем обработчика на событие OnEpilog, который будет вызывать Bitrix\Main\Page\Asset->AddCss() для нужных страниц.
Изменить цвет кнопки
9 мин
Пример посложнее: всплывающее окно со списком элементов
Задача – добавить в левое меню кнопку «Факты компании». При нажатии на кнопку должно открываться всплывающее окно с набором фактов о компании. Сам набор должен задаваться администратором портала в административном разделе портала без участия программистов.
Для решения задачи уже потребуется больше работы:
- Определить, к какому HTML-элементу можно привязаться для добавления кастомной кнопки для отображения фактов.
- Организовать "админку" для управления списком фактов, например, инфоблок.
- На обработчике события OnEpilog реализовать добавление кнопки, по клику на которую будет вызываться наше js-расширение.
- Создать js-расширение, которое будет показывать факт в pop-up окне.
- Создать контроллер, который будет возвращать содержимое для pop-up из компонента.
- Создать компонент, который будет возвращать факт из инфоблока и его оформление в HTML.
- Не забыть создать миграцию для переноса доработки на production :).
Пункт в меню и всплывающее окно со списком элементов
29 мин
Список ресурсов
Материалы для выполнения практики
- Исходный код примера (.zip)