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