При разработке на управляемых формах, особенно в веб-клиенте, мы часто сталкиваемся с необходимостью открыть пользователю список (например, справочник «Номенклатура» или «Контрагенты») для выбора одного или нескольких элементов, а затем обработать этот выбор в коде. Раньше для этого использовались модальные окна, блокирующие весь интерфейс программы. Однако современные стандарты разработки 1С и требования работы в веб-браузерах диктуют отказ от модальности.
Рассмотрим подробно, как правильно реализовать этот механизм, проанализируем паттерны асинхронной разработки на 1С от старых до новых, и разберем конкретные примеры кода, которые помогут нам решить эту задачу эффективно и безопасно.
Многие из нас привыкли к методу ОткрытьФормуМодально(). Он кажется простым: код останавливается, ждет выбора пользователя и сразу возвращает результат. Однако у этого подхода есть критические недостатки:
Поэтому мы разберем современные асинхронные методы работы.
Этот метод работает на всех версиях платформы 8.3 и является самым надежным для обеспечения совместимости. Суть метода заключается в том, что мы не «ждем» ответ в той же строке кода. Вместо этого мы говорим системе: «Открой форму, а когда пользователь что-то выберет, вызови вот эту процедуру». Если же стандартной формы выбора справочника недостаточно, в некоторых сценариях может пригодиться универсальная форма выбора типа.
Для этого нам понадобится объект ОписаниеОповещения. Давайте посмотрим, как это выглядит на практике. Предположим, у нас есть команда на форме, по которой нужно выбрать Контрагента.
Сначала напишем процедуру, которая инициирует открытие:
&НаКлиенте
Процедура ПодобратьКонтрагента(Команда)
// 1. Создаем "обещание" или описание того, что делать после закрытия формы выбора.
ОповещениеОВыборе = Новый ОписаниеОповещения("ОбработкаВыбораКонтрагента", ЭтотОбъект);
// 2. Готовим параметры для открытия формы.
ПараметрыОткрытия = Новый Структура;
ПараметрыОткрытия.Вставить("РежимВыбора", Истина);
// 3. Открываем форму, передавая параметры и наше оповещение.
ОткрытьФорму("Справочник.Контрагенты.ФормаВыбора",
ПараметрыОткрытия,
ЭтотОбъект,
,,,,
ОповещениеОВыборе);
КонецПроцедуры
Обратите внимание: код в процедуре ПодобратьКонтрагента выполнится и завершится сразу. Система просто откроет окно и «забудет» о нем до момента закрытия. Теперь нам нужно описать саму процедуру обработки результата.
&НаКлиенте
Процедура ОбработкаВыбораКонтрагента(РезультатВыбора, ДополнительныеПараметры) Экспорт
// Проверяем, выбрал ли что-то пользователь.
Если РезультатВыбора <> Неопределено Тогда
// РезультатВыбора содержит Ссылку на выбранный элемент.
Объект.Контрагент = РезультатВыбора;
Сообщить("Выбран контрагент: " + Строка(РезультатВыбора));
Иначе
Сообщить("Выбор отменен пользователем.");
КонецЕсли;
КонецПроцедуры
Представим ситуацию сложнее: мы выбираем номенклатуру для конкретной строки табличной части. Нам нужно знать, в какую именно строку поместить результат.
&НаКлиенте
Процедура ВыбратьТоварВСтроке(Элемент)
ТекущаяСтрокаТЧ = Элементы.Товары.ТекущиеДанные;
Если ТекущаяСтрокаТЧ = Неопределено Тогда
Возврат;
КонецЕсли;
Контекст = Новый Структура("ИдентификаторСтроки", ТекущаяСтрокаТЧ.ПолучитьИдентификатор());
Оповещение = Новый ОписаниеОповещения("ПослеВыбораТовара", ЭтотОбъект, Контекст);
ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора",
Новый Структура("РежимВыбора", Истина),
ЭтотОбъект,
,,,,
Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораТовара(Результат, Параметры) Экспорт
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
Идентификатор = Параметры.ИдентификаторСтроки;
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
Если СтрокаТЧ <> Неопределено Тогда
СтрокаТЧ.Номенклатура = Результат;
ЗаполнитьЦенуНаСервере(СтрокаТЧ.ПолучитьИдентификатор());
КонецЕсли;
КонецПроцедуры
Начиная с версии платформы 8.3.18, 1С ввела синтаксис, который делает асинхронный код похожим на синхронный. Чтобы освоить `Async/Await` на практике, мы используем ключевые слова Ждать (Await) и Асинх (Async).
&НаКлиенте
Асинх Процедура ПодобратьКонтрагента(Команда)
ПараметрыОткрытия = Новый Структура("РежимВыбора", Истина);
Попытка
Результат = Ждать ОткрытьФормуАсинх("Справочник.Контрагенты.ФормаВыбора", ПараметрыОткрытия, ЭтотОбъект);
Если Результат <> Неопределено Тогда
Объект.Контрагент = Результат;
Сообщить("Выбран: " + Строка(Результат));
Иначе
Сообщить("Пользователь отказался от выбора");
КонецЕсли;
Исключение
Сообщить("Произошла ошибка при открытии: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Часто возникает задача дать пользователю возможность выбрать сразу несколько позиций.
Правильный способ активировать множественный выбор при программном открытии:
&НаКлиенте
Процедура ПодобратьСписокТоваров(Команда)
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("РежимВыбора", Истина);
ПараметрыФормы.Вставить("МножественныйВыбор", Истина);
Оповещение = Новый ОписаниеОповещения("ОбработкаСпискаТоваров", ЭтотОбъект);
ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора",
ПараметрыФормы,
ЭтотОбъект,
,,,,
Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ОбработкаСпискаТоваров(Результат, Параметры) Экспорт
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого ВыбранныйТовар Из Результат Цикл
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Номенклатура = ВыбранныйТовар;
КонецЦикла;
ЗаполнитьЦеныДляВсехСтрок();
КонецПроцедуры
ОткрытьФорму).
Всегда передавайте ЭтотОбъект в качестве владельца. Если вам нужно изучить или изменить свойства элементов формы «на лету», используйте редактор форм в режиме предприятия.
Никогда не забывайте проверять Если Результат <> Неопределено, чтобы избежать ошибок при закрытии формы нажатием Esc.
Обработчик оповещения выполняется &НаКлиенте. Для сложных серверных операций вызывайте соответствующие функции модуля.
Процедура-обработчик должна быть доступна платформе.
Для решения задачи выбора значений программно мы выяснили следующее:
ОткрытьФормуМодально.ОписаниеОповещения для универсального решения, работающего везде.Ждать / Асинх.Неопределено и грамотно передаем контекст через структуры.