Команды и обработки на формах
Делаем кнопки рабочими: команды формы, их обработчики, вызов сервера по нажатию и обновление формы результатом.
Команда формы — это именованное действие, которое пользователь запускает вручную (обычно кнопкой). У команды есть обработчик — процедура, выполняющаяся при её вызове.
События из прошлого урока платформа запускает сама (открытие, изменение поля). Но многое пользователь должен инициировать осознанно: «Заполнить по шаблону», «Рассчитать скидку», «Отправить на согласование». Для таких действий существуют команды. Команда — это не кнопка. Кнопка — лишь один из способов вызвать команду; та же команда может жить и в командной панели, и в контекстном меню, и вызываться по горячей клавише.
Зачем это нужно на практике
Команды — это «глаголы» вашей формы: всё, что пользователь может сделать по своей воле. Разделение «команда ↔ кнопка» удобно тем, что одно действие можно разместить в нескольких местах интерфейса, не дублируя логику. Написали обработчик один раз — привязали к нему сколько угодно кнопок.
Команда и её обработчик
Команду добавляют в дереве формы (ветка «Команды»), задают имя и указывают процедуру-обработчик. Обработчик команды всегда клиентский и помечается директивой &НаКлиенте — ведь нажатие происходит у пользователя.
&НаКлиенте
Процедура ЗаполнитьТоварыПоОстаткам(Команда)
// Обработчик команды: параметр "Команда" — сама вызванная команда.
// Тяжёлую работу (запрос к базе) поручаем серверу.
ЗаполнитьТоварыПоОстаткамНаСервере();
КонецПроцедуры
Параметр Команда — ссылка на вызванную команду (полезно, если один обработчик обслуживает несколько команд и надо различить, какая нажата, через Команда.Имя).
Кнопка: как команда попадает на экран
Чтобы команда стала доступной, на форму добавляют элемент-кнопку и в её свойстве ИмяКоманды указывают нужную команду. Заголовок кнопки, картинку и расположение настраивают в свойствах элемента. Одну команду можно привязать к нескольким кнопкам сразу.
&НаКлиенте
Процедура ОбновитьКурсВалюты(Команда)
// Кнопка "Обновить курс" вызывает эту команду.
// Меняем внешний вид кнопки на время работы — через элемент.
Элементы.ОбновитьКурс.Доступность = Ложь;
Попытка
Объект.Курс = ПолучитьКурсНаДату(Объект.Валюта, Объект.Дата);
Исключение
Сообщить("Не удалось получить курс: " + ОписаниеОшибки());
КонецПопытки;
Элементы.ОбновитьКурс.Доступность = Истина;
КонецПроцедуры
Вызов серверной процедуры по нажатию
Ключевой паттерн: кнопка нажимается на клиенте, но настоящая работа (запросы, чтение и запись данных) идёт на сервере. Поэтому клиентский обработчик команды почти всегда вызывает серверную процедуру. Обычно берут &НаСервере, когда серверу нужно менять реквизиты формы, и результат сразу окажется на клиенте.
&НаКлиенте
Процедура ЗаполнитьТоварыПоОстаткам(Команда)
ЗаполнитьТоварыПоОстаткамНаСервере(); // клиент просит сервер
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьТоварыПоОстаткамНаСервере()
// Серверная процедура с контекстом: видит реквизиты формы (Объект).
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Остатки
|ГДЕ
| Остатки.КоличествоОстаток > 0";
Запрос.УстановитьПараметр("Дата", Объект.Дата);
Запрос.УстановитьПараметр("Склад", Объект.Склад);
Объект.Товары.Очистить();
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Номенклатура = Выборка.Номенклатура;
НоваяСтрока.Количество = Выборка.Количество;
КонецЦикла;
КонецПроцедуры
Здесь серверная процедура прямо меняет табличную часть Объект.Товары. Так как она с контекстом, изменённые реквизиты автоматически вернутся на клиент, и пользователь сразу увидит заполненную таблицу — форма «обновилась» сама.
Передача параметров и обновление формы
Если серверу не нужен весь контекст формы, а только пара значений, эффективнее вызвать &НаСервереБезКонтекста и передать параметры явно, а результат применить на клиенте. Это снижает объём пересылаемых данных.
&НаКлиенте
Процедура РассчитатьСкидку(Команда)
// Передаём на сервер только сумму, получаем процент скидки.
Процент = ВычислитьПроцентСкидки(Объект.СуммаДокумента);
Объект.Скидка = Объект.СуммаДокумента * Процент / 100;
// Обновляем оформление поля скидки в зависимости от результата.
Элементы.Скидка.ЦветТекста = ?(Процент > 0, ЦветаСтиля.ПоложительныйТекст, ЦветаСтиля.ОбычныйТекст);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ВычислитьПроцентСкидки(Сумма)
// Внеконтекстно: контекст формы не пересылается, только параметр "Сумма".
Если Сумма >= 100000 Тогда
Возврат 10;
ИначеЕсли Сумма >= 50000 Тогда
Возврат 5;
Иначе
Возврат 0;
КонецЕсли;
КонецФункции
Обратите внимание на тернарный оператор ?(Условие, ЗначениеЕслиИстина, ЗначениеЕслиЛожь) — компактный способ выбрать одно из двух значений прямо в выражении.
Как это работает
Нажатие кнопки платформа превращает в вызов связанной команды, а команду — в вызов её обработчика (всегда на клиенте). Дальше клиентский код решает, нужен ли сервер. При контекстном вызове (&НаСервере) платформа упаковывает реквизиты формы, отправляет на сервер, там ваш код их меняет, и обновлённый пакет возвращается на клиент — так форма обновляется. При внеконтекстном вызове пересылаются только явные параметры и возвращаемое значение, а реквизиты формы сервер не видит. Понимание этой «дороги данных» — то, что отличает крепкого разработчика форм от новичка, у которого «кнопка тормозит».
Частые ошибки
- Писать серверный обработчик команды. Пометить обработчик команды
&НаСерверенельзя — он должен быть клиентским. Серверную работу выносят в отдельную процедуру и вызывают из клиентского обработчика. - Менять реквизиты формы во внеконтекстном вызове. В
&НаСервереБезКонтекстареквизитов формы (в том числеОбъект) не видно — присвоение им там невозможно. Нужен контекстный вызов или возврат значения на клиент. - Забыть очистить таблицу перед заполнением. Без
Объект.Товары.Очистить()команда «Заполнить» будет добавлять строки к уже имеющимся, дублируя данные. - Тяжёлые запросы без обратной связи. Долгая команда без блокировки кнопки провоцирует повторные нажатия. Полезно на время работы ставить
Элементы.Кнопка.Доступность = Ложь.
Итоги
- Команда — это действие пользователя; кнопка — лишь способ его вызвать (одну команду можно привязать к нескольким кнопкам).
- Обработчик команды всегда клиентский (
&НаКлиенте) и обычно вызывает серверную процедуру для работы с базой. - Контекстный вызов
&НаСерверевидит и меняет реквизиты формы — форма обновляется автоматически;&НаСервереБезКонтекстабыстрее, но работает только через явные параметры. - Перед заполнением табличной части её очищают; долгие команды сопровождают блокировкой кнопки и обработкой ошибок через
Попытка ... Исключение.