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