Почему разные формы одной обработки содержат разные значения реквизитов и как передать данные между ними

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе с обычных форм на управляемые разработчики часто сталкиваются с неожиданным поведением: во внешней обработке созданы две формы, в обеих выведены одни и те же реквизиты объекта и включено автосохранение, однако при открытии второй формы из первой значения реквизитов оказываются совершенно другими или вовсе пустыми. Выясним причину этой особенности архитектуры клиент-серверной модели 1С и подробно разберем правильные способы синхронизации данных.

Архитектурная причина: почему реквизиты не синхронизируются автоматически

Проанализируем, как устроен жизненный цикл данных управляемой формы. В обычном приложении («толстый клиент») форма работала напрямую с объектом в оперативной памяти. В управляемом интерфейсе парадигма принципиально иная:

  1. Отсутствие единого объекта на клиенте. На стороне клиента экземпляра реального объекта не существует вовсе. Основной реквизит формы (как правило, Объект) представляет собой специализированную коллекцию — ДанныеФормыСтруктура. Каждая открытая форма создает свою собственную, полностью изолированную структуру данных в памяти клиента.
  2. Жизненный цикл объекта на сервере при создании формы. Когда форма инициализируется, платформа на сервере временно создает экземпляр объекта обработки, выполняет код модуля объекта и обработчик ПриСозданииНаСервере, затем конвертирует реквизиты объекта в ДанныеФормыСтруктура и сразу же уничтожает экземпляр объекта в серверной памяти. Соответственно, при открытии второй формы обработка инициализируется заново «с нуля».
  3. Изоляция механизма автоматического сохранения настроек. Стандартное сохранение значений реквизитов формы выполняется платформой в разрезе полного имени формы (например, ВнешняяОбработка.МояОбработка.Форма.Форма1). Поскольку имена у двух форм разные, система сохраняет и восстанавливает их настройки в совершенно независимые ветки хранилища. Форма 2 физически не видит настройки, сохраненные Формой 1.

Способ 1. Передача данных через стандартный параметр «ЗначенияЗаполнения»

Рассмотрим штатный и наиболее надежный механизм платформы 1С для инициализации реквизитов новой формы при открытии. Метод ОткрытьФорму позволяет передать системный параметр ЗначенияЗаполнения.

Разберем по шагам, как это работает в кодовой базе. На стороне первой формы сформируем структуру с реквизитами и передадим ее при вызове:


&НаКлиенте
Процедура ОткрытьВторуюФорму(Команда)
    ПараметрыОткрытия = Новый Структура;
    
    ЗначенияЗаполнения = Новый Структура;
    ЗначенияЗаполнения.Вставить("Организация", Объект.Организация);
    ЗначенияЗаполнения.Вставить("Склад", Объект.Склад);
    ЗначенияЗаполнения.Вставить("Период", Объект.Период);
    
    ПараметрыОткрытия.Вставить("ЗначенияЗаполнения", ЗначенияЗаполнения);
    
    ОткрытьФорму("ВнешняяОбработка.МояОбработка.Форма.ФормаВторая", ПараметрыОткрытия, ЭтотОбъект);
КонецПроцедуры

Платформа 1С автоматически сопоставит переданные ключи структуры с реквизитами основного объекта открываемой формы и заполнит их еще до выполнения серверных процедур инициализации.

Способ 2. Передача произвольных параметров и обработка в «ПриСозданииНаСервере»

Если требуется выполнить дополнительную валидацию, рассчитать зависимые данные или передать сложные структуры, воспользуемся явной передачей через произвольные параметры формы. Посмотрим на реализацию вызова:


&НаКлиенте
Процедура ОткрытьФормуСПараметрами(Команда)
    ПараметрыОткрытия = Новый Структура;
    
    ДанныеПередачи = Новый Структура;
    ДанныеПередачи.Вставить("Организация", Объект.Организация);
    ДанныеПередачи.Вставить("Склад", Объект.Склад);
    
    ПараметрыОткрытия.Вставить("ДанныеПередачи", ДанныеПередачи);
    
    ОткрытьФорму("ВнешняяОбработка.МояОбработка.Форма.ФормаВторая", ПараметрыОткрытия, ЭтотОбъект);
КонецПроцедуры

Теперь перейдем в модуль второй формы и разберем серверный обработчик ПриСозданииНаСервере. Здесь мы применим метод ЗаполнитьЗначенияСвойств:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Если Параметры.Свойство("ДанныеПередачи") Тогда
        ЗаполнитьЗначенияСвойств(Объект, Параметры.ДанныеПередачи);
    КонецЕсли;
КонецПроцедуры

Такой подход обеспечивает контроль над процессом загрузки данных и гарантирует, что они будут встроены в контекст до окончательного формирования интерфейса для пользователя.

Почему не рекомендуется прямое заполнение через «ПолучитьФорму»

В коде иногда встречается подход с получением формы и прямым изменением ее реквизитов на клиенте:


ФормаУпр = ПолучитьФорму("ВнешняяОбработка.МояОбработка.Форма.ФормаВторая");
ФормаУпр.Объект.Реквизит1 = Объект.Реквизит1;
ФормаУпр.Открыть();

Проанализируем, почему такой способ несет скрытые риски и считается устаревшим:

  1. Метод ПолучитьФорму на клиенте нарушает принятые стандарты разработки управляемого интерфейса в пользу метода ОткрытьФорму.
  2. При вызове ПолучитьФорму серверное событие ПриСозданииНаСервере выполняется сразу же. Если внутри этого события заложена логика, зависящая от переданных значений реквизитов, она выполнится вхолостую со старыми значениями, так как присвоение полей на клиенте произойдет позже.
  3. Прямая модификация реквизитов через точку не активирует клиентские события элементов управления ПриИзменении, что может привести к несогласованности зависимых полей формы.

Организация общего хранилища настроек для двух форм

Если цель состоит именно в том, чтобы обе формы сохраняли и читали одни и те же настройки пользователя между запусками программы, стандартные флаги автосохранения реквизитов не подойдут из-за изоляции по имени формы. В такой ситуации используем ХранилищеОбщихНастроек или ХранилищеСистемныхНастроек.

Посмотрим, как организовать общее сохранение и восстановление настроек по единому ключу.

В событии ПриСозданииНаСервере обеих форм реализуем чтение сохраненных данных:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    КлючОбъекта = "ВнешняяОбработка_МояОбработка";
    КлючНастроек = "ОсновныеПараметры";
    
    Настройки = ХранилищеОбщихНастроек.Загрузить(КлючОбъекта, КлючНастроек);
    Если ТипЗнч(Настройки) = Тип("Структура") Тогда
        ЗаполнитьЗначенияСвойств(Объект, Настройки);
    КонецЕсли;
КонецПроцедуры

Сохранение значений выполняем в процедуре записи настроек (например, перед закрытием формы или по отдельной команде):


&НаСервере
Процедура СохранитьНастройкиНаСервере()
    КлючОбъекта = "ВнешняяОбработка_МояОбработка";
    КлючНастроек = "ОсновныеПараметры";
    
    Настройки = Новый Структура;
    Настройки.Вставить("Организация", Объект.Организация);
    Настройки.Вставить("Склад", Объект.Склад);
    
    ХранилищеОбщихНастроек.Сохранить(КлючОбъекта, КлючНастроек, Настройки);
КонецПроцедуры

Использование единого ключа объекта и ключа настроек связывает обе формы с одним общим источником данных в базе.

Альтернативное решение: объединение экранов в одну форму

Если обе формы оперируют одинаковым набором реквизитов и вторая форма по сути представляет собой пошаговый мастер или детальный режим первой, наиболее элегантным архитектурным решением является отказ от разделения на две формы.

Создадим одну общую форму, добавим группу вида «Страницы» (с отключенным отображением закладок) и расположим элементы первого и второго интерфейсов на разных страницах. Переключение экранов реализуется одной строкой кода на клиенте:


&НаКлиенте
Процедура ПерейтиКоВторомуШагу(Команда)
    Элементы.ГруппаСтраницы.ТекущаяСтраница = Элементы.СтраницаВторойШаг;
КонецПроцедуры

При таком подходе данные объекта Объект остаются едиными, исключаются лишние клиент-серверные взаимодействия для синхронизации, а стандартный механизм автосохранения формы работает корректно и предсказуемо.

← На главную