Кастомизация crm функционала на декораторах
Декоратор можно использовать для кастомизации функционала crm без правок ядра.
В общем виде декоратор выглядит так:
const bxCrmEntityMethodDecorator = function (func, context) {
return function (...params) {
return func.apply(context, params);
}
}
BX.Crm.Entity.method = bxCrmEntityMethodDecorator(BX.Crm.Entity.method, BX.Crm.Entity);
Идея - переопределить стандартный битровый метод, который внутри себя вызывает оригинал. Параметр context используется чтоб сохранить this внутри метода.
Варианты определения декораторов
- использование
callесли известно количество передаваемых в функцию параметров - использвание
applyи arguments для любого количества параметров - использвание
applyи spread синтаксиса
Декорирование поведения объекта
Декоратор применяется к существующему объекту. Для начала объект надо где-то взять. Некоторые классы бросают событие при создании либо инициализации. Можно подписаться на такое событие, в обработчике навесить декоратор.
Декорирование создания объекта
Не все объекты бросают события при создании. В таком случае можно навесить декоратор на сам конструктор, в декораторе бросить событие. Далее подписаться на это событие, а в обработчике события, имея созданный объект, декорировать нужные методы.
Пример: необходимо в списке сделок навесить обработчик на переключение стадий, который будет показывать окно подтверждения.
За функционал переключения стадий отвечает класс BX.CrmProgressControl. При переключении стадий он бросает событие перед самым ajax'ом в тот момент, когда уже сработала анимация переключения. Нам такое не подходит. События на создание объект не бросает. Организуем сами.
Для создания объекта используется метод BX.CrmProgressControl.create:
(function () {
/**
* Декоратор конструктора объекта
*
* Декоратор вызывает оригинальный метод для создания объекта.
* Далее бросает событие, куда параметром пробрасывает созданный объект.
* Так как оригинальный метод возвращает объект через return, то сделаем то же самое чтоб повторить поведение
*
* @param func
* @param context
* @return {function(*, *): *}
*/
const crmProgressControlCreateDecorator = function (func, context) {
return function (id, settings) {
const crmProgressControl = func.call(context, id, settings); // вызываем оригинальный метод create
BX.onCustomEvent(crmProgressControl, 'CrmProgressControlCreate', [crmProgressControl]); // бросаем кастомное событие, куда передаём созданный объект
return crmProgressControl;
};
}
/**
* Декоратор обработчика смены стадии
*
* Декоратор вызывает диалог подтверждения.
* В случае подтверждения вызывается стандартный обработчик.
* Иначе ничего не происходит
*
* @param func
* @param context
* @return {(function(*): void)|*}
*/
const setCurrentStepDecorator = function (func, context) {
return function (step) {
BX.UI.Dialogs.MessageBox.confirm(
'Вы точно хотите сменить стадию?',
'Смена стадии',
function (messageBox) {
func.call(context, step); // вызов оригинального метода-обработчика
messageBox.close();
},
'Да',
);
}
}
// подменяем конструктор на декоратор
BX.CrmProgressControl.create = crmProgressControlCreateDecorator(BX.CrmProgressControl.create, BX.CrmProgressControl);
BX.addCustomEvent('CrmProgressControlCreate', function (crmProgressControl) {
// событие срабатывает в тот момент, когда объект уже создан. Можно декорировать методы объекта
crmProgressControl.setCurrentStep = setCurrentStepDecorator(crmProgressControl.setCurrentStep, crmProgressControl);
});
})();
Замечания
Декораторы можно подключать как BX JS расширения \Bitrix\Main\UI\Extension::load().
В таком случае классы уже будут описаны, а вот объекты ещё не созданы.