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