Как вызвать обработчик события элемента формы по имени строки в веб-клиенте 1С 8.3 без использования Выполнить

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью динамического управления поведением формы. Типичная задача: программно перехватить стандартное событие элемента (например, ПриИзменении), выполнить собственный код и затем вызвать оригинальный типовой обработчик, имя которого было получено через метод ПолучитьДействие().

Однако при работе в веб-клиенте разработчики упираются в серьезное ограничение: оператор Выполнить(), позволяющий исполнить строку кода, в этом режиме запуска не поддерживается и вызывает исключение. В данной статье мы подробно разберем, почему это происходит и какие существуют обходные пути для реализации подобной задачи, не прибегая к расширениям конфигурации.

Проблема использования Выполнить() в веб-клиенте

Разберем ситуацию: на управляемой форме нам нужно подменить типовое действие. Мы используем следующий алгоритм:

  1. Сохраняем имя текущего обработчика с помощью ПолучитьДействие("ПриИзменении").
  2. Устанавливаем свой метод через УстановитьДействие("ПриИзменении", "МойМетод").
  3. Внутри МойМетод выполняем нужные действия и пытаемся вызвать старый метод по сохраненному имени.

В тонком клиенте код Выполнить(ИмяСтарогоМетода + "(Элемент)") отработает успешно. Но веб-клиент, работающий в браузере, транслирует код 1С в JavaScript. Из соображений безопасности и особенностей компиляции динамическое исполнение произвольного кода (аналог eval() в JS) для объектов формы ограничено. Платформа просто не знает, какой метод вызвать, так как в веб-клиенте имена методов могут быть обфусцированы или оптимизированы.

Решение 1: Метод «Диспетчеризация» (Case-структура)

Если мы не можем вызвать метод динамически, нам придется вызвать его статически. Рассмотрим ситуацию, когда мы заранее знаем возможные имена обработчиков (или их конечное количество в конкретной форме). Проанализируем пример создания «шлюза» для вызова типовых процедур.

Создадим в модуле формы процедуру-диспетчер, которая будет принимать имя метода и параметры:


&НаКлиенте
Процедура ИсполнитьМетодПоИмени(ИмяМетода, Элемент)
    
    Если ИмяМетода = "ПодразделениеПриИзменении" Тогда
        ПодразделениеПриИзменении(Элемент);
    ИначеЕсли ИмяМетода = "ОрганизацияПриИзменении" Тогда
        ОрганизацияПриИзменении(Элемент);
    ИначеЕсли ИмяМетода = "СотрудникПриИзменении" Тогда
        СотрудникПриИзменении(Элемент);
    // Добавьте другие возможные обработчики
    КонецЕсли;
    
КонецПроцедуры

Плюсы этого подхода: полная совместимость с веб-клиентом и сохранение типизации. Минусы: необходимость жестко прописывать имена методов, что снижает универсальность решения при обновлении конфигурации.

Решение 2: Использование «ОписанияОповещения»

Платформа 1С предлагает механизм ОписаниеОповещения, который позволяет передать ссылку на процедуру для последующего вызова. Выясним, как это может помочь. Метод ВыполнитьОбработкуОповещения() может вызвать процедуру по имени, переданному в объекте описания.

Однако здесь есть два критических требования:

  1. Целевая процедура обязательно должна быть экспортной (содержать ключевое слово Экспорт).
  2. Сигнатура процедуры должна соответствовать стандарту (принимать определенное количество параметров, обычно включая ДополнительныеПараметры).

Если типовой метод ПриИзменении(Элемент) не является экспортным (что чаще всего и бывает), этот метод не сработает напрямую. Если же вы имеете право добавлять Экспорт к типовым методам, схема будет выглядеть так:


&НаКлиенте
Процедура МойПодключаемыйОбработчик(Элемент)
    
    // Своя логика
    Сообщить("Выполнен мой код");
    
    // Вызов оригинала через ОписаниеОповещения
    ИмяТиповогоМетода = "ТиповойМетодПриИзменении"; // Получено ранее через ПолучитьДействие
    Оповещение = Новый ОписаниеОповещения(ИмяТиповогоМетода, ЭтотОбъект);
    ВыполнитьОбработкуОповещения(Оповещение, Элемент);
    
КонецПроцедуры

Решение 3: Программная имитация интерактивного события

Существует более сложный, но интересный способ — заставить платформу саму вызвать событие элемента. Посмотрим на логику использования временной подмены обработчиков. Мы можем программно «дернуть» событие, если изменим данные, с которыми связан элемент, или используем специфические методы платформы.

Например, можно попробовать использовать механизм ОповеститьОВыборе(), если связать элемент с вспомогательной формой. Однако наиболее надежным способом программной инициации события ПриИзменении является изменение значения реквизита, связанного с полем ввода, с последующим вызовом ОповеститьОбИзменении(Тип("ВашТип")). Но это вызовет цепочку событий во всех открытых формах, что не всегда приемлемо.

Решение 4: Кэширование методов через дополнительные реквизиты

Разберем практический пример реализации перехвата через общий модуль. Допустим, нам нужно в нескольких формах перехватить выбор подразделения. Для этого на сервере мы создаем программный реквизит-коллекцию, где будем хранить карту соответствий «Элемент -> Оригинальное Действие».

Рассмотрим серверный код инициализации:


// В общем модуле (вызывается из ПриСозданииНаСервере формы)
Процедура ПерехватитьСобытия(Форма)
    
    // Создаем программный реквизит для хранения имен методов
    Типы = Новый Массив;
    Типы.Добавить(Тип("СписокЗначений"));
    
    НовыеРеквизиты = Новый Массив;
    НовыеРеквизиты.Добавить(Новый РеквизитФормы("ХранилищеСобытий", Новый ОписаниеТипов(Типы)));
    Форма.ИзменитьРеквизиты(НовыеРеквизиты);
    
    Элемент = Форма.Элементы.Найти("Подразделение");
    Если Элемент <> Неопределено Тогда
        ОригинальноеДействие = Элемент.ПолучитьДействие("ПриИзменении");
        Форма.ХранилищеСобытий.Добавить(Элемент.Имя, ОригинальноеДействие);
        
        // Устанавливаем свой обработчик (он должен быть в контексте формы или общем модуле)
        Элемент.УстановитьДействие("ПриИзменении", "МойОбщийОбработчикПриИзменении");
    КонецЕсли;
    
КонецПроцедуры

И клиентский обработчик в модуле формы (или общем модуле, если используется ПодключитьОбработчикОповещения):


&НаКлиенте
Процедура МойОбщийОбработчикПриИзменении(Элемент)
    
    // Наша логика
    ОбновитьСвязанныеДанные(Элемент);
    
    // Попытка вызова оригинала
    НайденноеСобытие = ХранилищеСобытий.НайтиПоЗначению(Элемент.Имя);
    Если НайденноеСобытие <> Неопределено И ЗначениеЗаполнено(НайденноеСобытие.Представление) Тогда
        // В веб-клиенте здесь всё равно придется использовать Диспетчер (см. Решение 1)
        ИсполнитьТиповойМетод(НайденноеСобытие.Представление, Элемент);
    КонецЕсли;
    
КонецПроцедуры

Выводы и рекомендации

Проанализировав ситуацию, мы приходим к неутешительному, но важному выводу: полного динамического эквивалента Выполнить() для неэкспортных методов формы в веб-клиенте не существует. Это архитектурное ограничение платформы 1С.

Если вы столкнулись с такой задачей, рекомендуется следовать следующим правилам:

Таким образом, для веб-клиента разработка требует более явного определения связей между кодом и элементами управления, исключая «магию» динамического исполнения строк.

← На главную