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