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