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