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