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