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