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