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