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