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