Обработчики событий формы

Учимся «оживлять» форму: перехватывать открытие, ввод пользователя и изменение полей через обработчики событий.

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

Форма без обработчиков — это просто картинка с полями. Логику ей дают события. Пользователь открыл форму — сработало одно событие; поменял поле «Количество» — другое; нажал «Провести» — третье. В каждый такой момент платформа ищет у формы соответствующую процедуру-обработчик и, если находит, выполняет ваш код. Это событийная модель: вы не пишете «главный цикл», вы пишете реакции на события.

Зачем это нужно на практике

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

Где на клиенте, где на сервере

Обработчики бывают клиентские (&НаКлиенте) и серверные (&НаСервере). Это ключевое различие управляемых форм. Клиентский код исполняется у пользователя (в тонком или веб-клиенте), серверный — на сервере 1С, где есть доступ к базе данных.

Правило: к базе данных (справочникам, документам, запросам) можно обращаться только на сервере. На клиенте базы «не видно» — там только интерфейс и то, что уже загружено в реквизиты.

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    // Клиентский обработчик: выполняется у пользователя.
    // Здесь НЕТ прямого доступа к базе — только к реквизитам формы.
    Если НЕ ЗначениеЗаполнено(Объект.Дата) Тогда
        Сообщить("Внимание: не заполнена дата документа.");
    КонецЕсли;
КонецПроцедуры

ПриСозданииНаСервере: подготовка формы

Самый ранний и самый важный обработчик — ПриСозданииНаСервере. Он срабатывает один раз, когда форма создаётся на сервере, ещё до показа пользователю. Здесь заполняют значения по умолчанию, настраивают видимость элементов, готовят данные — потому что именно тут есть доступ к базе.

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Срабатывает при создании формы на сервере (до открытия).
    // Есть доступ к базе — заполняем реквизиты значениями по умолчанию.
    Если Объект.Ссылка.Пустая() Тогда // это новый, ещё не записанный документ
        Объект.Дата = ТекущаяДата();
        Объект.Организация = Справочники.Организации.ОсновнаяОрганизация();
        Объект.Валюта = Константы.ВалютаУчёта.Получить();
    КонецЕсли;

    // Настроим внешний вид: спрячем блок скидок для розницы.
    Элементы.ГруппаСкидки.Видимость = (Объект.ВидОперации <> ПредопределённоеЗначение("Перечисление.ВидыОпераций.Розница"));
КонецПроцедуры

Параметр Отказ есть у многих обработчиков: если внутри установить Отказ = Истина, платформа отменит текущее действие (например, не откроет форму или не запишет объект).

ПриОткрытии: форма уже на экране

Обработчик ПриОткрытии — клиентский. Он срабатывает после ПриСозданииНаСервере, когда форма уже показана пользователю. Здесь уместны действия, требующие интерфейса: показать предупреждение, установить фокус, открыть подсказку. Прямого доступа к базе тут нет.

ПриИзменении реквизита: реакция на ввод

Когда пользователь меняет значение в поле, у соответствующего элемента срабатывает событие ПриИзменении. Это самый частый сценарий: ввёл цену — пересчиталась сумма. Обработчик получает параметр Элемент — ссылку на изменившийся элемент.

&НаКлиенте
Процедура ЦенаПриИзменении(Элемент)
    // Пользователь изменил цену в текущей строке табличной части "Товары".
    СтрокаТовара = Элементы.Товары.ТекущиеДанные;
    Если СтрокаТовара <> Неопределено Тогда
        СтрокаТовара.Сумма = СтрокаТовара.Количество * СтрокаТовара.Цена;
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура КоличествоПриИзменении(Элемент)
    // Та же логика для количества — пересчитываем сумму строки.
    СтрокаТовара = Элементы.Товары.ТекущиеДанные;
    Если СтрокаТовара <> Неопределено Тогда
        Если СтрокаТовара.Количество <= 0 Тогда
            СтрокаТовара.Количество = 1; // не даём ввести ноль или минус
        КонецЕсли;
        СтрокаТовара.Сумма = СтрокаТовара.Количество * СтрокаТовара.Цена;
    КонецЕсли;
КонецПроцедуры

Типичный сценарий обработки ввода

Часто клиентское событие должно обратиться к базе (например, при выборе контрагента подтянуть его данные). Тогда клиентский обработчик вызывает серверную процедуру. Чтобы не гонять весь контекст формы, используют внеконтекстный серверный вызов &НаСервереБезКонтекста — он быстрее, так как не пересылает реквизиты формы туда-обратно.

&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
    // Клиент реагирует на выбор контрагента и просит сервер о данных.
    Если ЗначениеЗаполнено(Объект.Контрагент) Тогда
        Данные = ПолучитьДанныеКонтрагента(Объект.Контрагент);
        Объект.ДоговорПоУмолчанию = Данные.Договор;
        Объект.Скидка = Данные.Скидка;
    КонецЕсли;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьДанныеКонтрагента(Контрагент)
    // Серверная функция без контекста формы: обращается к базе.
    Результат = Новый Структура("Договор, Скидка");
    Результат.Договор = Контрагент.ОсновнойДоговор;
    Результат.Скидка = Контрагент.ПроцентСкидки;
    Возврат Результат;
КонецФункции

Как это работает

Платформа связывает событие с обработчиком по имени процедуры, указанному в свойствах элемента или формы (палитра свойств, вкладка «События»). Когда наступает событие, платформа проверяет директиву компиляции обработчика. Клиентское событие (нажатие, ввод) обслуживается клиентским обработчиком; попытка повесить на кнопку прямой серверный обработчик приведёт к ошибке — сначала клиент, потом при необходимости вызов сервера. Именно поэтому связка «клиентский обработчик → серверная процедура» так распространена.

Частые ошибки

  • Обращение к базе на клиенте. Написать Справочники.Товары.НайтиПоКоду(...) в клиентском обработчике — ошибка: на клиенте базы нет. Такой код выносят в серверную процедуру.
  • Тяжёлая логика в ПриОткрытии. Долгие расчёты и запросы в ПриОткрытии тормозят показ формы. Всё, что можно, делают в ПриСозданииНаСервере заранее.
  • Забыть проверку ТекущиеДанные. В обработчике строки таблицы Элементы.Товары.ТекущиеДанные может вернуть Неопределено (нет активной строки) — без проверки будет ошибка.
  • Лишний контекстный вызов сервера. Использовать &НаСервере там, где хватило бы &НаСервереБезКонтекста, — значит зря пересылать весь контекст формы и терять в скорости.

Итоги

  • Логику форме дают обработчики событий — процедуры, которые платформа вызывает в нужный момент.
  • ПриСозданииНаСервере — подготовка формы и доступ к базе; ПриОткрытии — клиентские действия после показа.
  • ПриИзменении реквизита — реакция на ввод пользователя (типовой пример: пересчёт суммы).
  • К базе обращаемся только на сервере; частый паттерн — клиентский обработчик вызывает &НаСервереБезКонтекста-функцию.
Проверьте себя
1. В каком обработчике правильнее всего заполнить реквизиты документа значениями по умолчанию из базы (организация, валюта учёта)?
AПриОткрытии (клиентский)
BПриСозданииНаСервере (серверный)
CЦенаПриИзменении (клиентский)
DВ любом клиентском обработчике
2. Клиентскому обработчику ПриИзменении понадобились данные из базы. Как это правильно сделать?
AПрямо в обработчике вызвать Справочники.Контрагенты.НайтиПоКоду()
BВызвать из клиента серверную процедуру (например, &НаСервереБезКонтекста)
CПометить весь обработчик директивой &НаСервере
DОбратиться к базе нельзя, нужно просить пользователя ввести всё вручную