Как передать таблицу значений с сервера на тонкий клиент в 1С:Предприятие 8.3

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

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

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


Почему возникает ошибка передачи таблицы значений?

Давайте проанализируем архитектуру управляемого приложения. Тонкий клиент спроектирован как максимально «легкое» приложение. Он должен быстро запускаться, потреблять минимум оперативной памяти и иметь возможность работать через медленные каналы связи (например, по протоколу HTTP/HTTPS).

Объект типа ТаблицаЗначений является тяжеловесным серверным объектом. Он содержит методы для работы с индексами, поиска данных, сортировки и выгрузки колонок. Если бы этот объект передавался на клиент, платформе приходилось бы тащить за собой огромный объем системных библиотек для его поддержки, что полностью нарушило бы концепцию легковесного клиента. Именно поэтому тип ТаблицаЗначений доступен исключительно на Сервере, в Толстом клиенте и во Внешнем соединении.

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


Способ 1. Преобразование таблицы значений в массив структур

Это самый популярный, гибкий и простой способ, когда нам нужно просто прочитать данные таблицы на клиенте или совершить с ними несложные манипуляции.

Типы данных Массив и Структура отлично поддерживаются на клиенте. Каждая строка таблицы преобразуется в отдельную структуру, где ключом является имя колонки, а значением — значение ячейки. Все эти структуры складываются в один общий массив.

Рассмотрим, как реализовать это вручную на сервере:


&НаСервере
Функция ПолучитьТаблицуКакМассивСтруктур()
    
    // Создаем тестовую таблицу значений на сервере
    ТЗ = Новый ТаблицаЗначений;
    ТЗ.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
    ТЗ.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
    ТЗ.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
    
    // Заполняем таблицу какими-то данными (например, из запроса)
    НоваяСтрока = ТЗ.Добавить();
    НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоКоду("00000001");
    НоваяСтрока.Количество = 10;
    НоваяСтрока.Цена = 100;
    
    // Преобразуем таблицу значений в массив структур
    ТаблицаМассивом = Новый Массив;
    Для Каждого СтрокаТЗ Из ТЗ Цикл
        СтруктураСтроки = Новый Структура;
        Для Каждого Колонка Из ТЗ.Колонки Цикл
            СтруктураСтроки.Вставить(Колонка.Имя, СтрокаТЗ[Колонка.Имя]);
        КонецЦикла;
        ТаблицаМассивом.Добавить(СтруктураСтроки);
    КонецЦикла;
    
    Возврат ТаблицаМассивом;
    
КонецФункции

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


// Использование встроенного метода БСП
МассивСтруктур = ОбщегоНазначения.ТаблицаЗначенийВМассив(ПолученнаяТЗ);

На клиенте мы получаем обычный массив, по которому можем легко пройтись циклом:


&НаКлиенте
Процедура ОбработатьДанныеНаКлиенте()
    
    ДанныеТаблицы = ПолучитьТаблицуКакМассивСтруктур();
    
    Для Каждого СтрокаДанных Из ДанныеТаблицы Цикл
        Сообщить("Товар: " + СтрокаДанных.Номенклатура + ", Кол-во: " + СтрокаДанных.Количество);
    КонецЦикла;
    
EndПроцедуры

Способ 2. Использование реквизита управляемой формы

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

На клиенте этот реквизит позиционируется как объект типа ДанныеФормыКоллекция. Платформа автоматически берет на себя синхронизацию данных между клиентом и сервером при вызове серверных процедур.

Давайте разберем по шагам, как правильно с этим работать, чтобы избежать ошибок, с которыми столкнулся автор темы:

  1. В редакторе формы (в правом верхнем окне «Реквизиты») добавьте новый реквизит формы. Дайте ему имя, например, АдресаДоставки, и укажите тип Таблица значений.
  2. Важно: Обязательно добавьте колонки внутрь этого реквизита в дереве реквизитов! Если вы оставите таблицу «пустой», то на клиенте вы не увидите данных, даже если загрузите их на сервере.
  3. Перетащите созданный реквизит формы на дерево элементов (в левое окно) — платформа автоматически создаст элемент интерфейса типа Таблица.

После этого на сервере вы можете легко заполнять этот реквизит с помощью встроенных методов:


&НаСервере
Процедура ЗаполнитьРеквизитФормыНаСервере()
    
    // Получаем реальную ТаблицуЗначений из запроса
    Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Код ИЗ Справочник.Номенклатура ГДЕ НЕ ПометкаУдаления");
    ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
    
    // Загружаем данные в наш реквизит формы. 
    // Названия колонок в ТаблицаРезультата должны совпадать с колонками реквизита формы!
    ЗначениеВРеквизитФормы(ТаблицаРезультата, "АдресаДоставки");
    
КонецПроцедуры

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


&НаСервере
Процедура СохранитьДанныеИзРеквизита()
    
    // Преобразуем данные формы обратно в полноценную серверную ТаблицуЗначений
    ТаблицаДляЗаписи = ДанныеФормыВЗначение(АдресаДоставки, Тип("ТаблицаЗначений"));
    
    // Теперь мы можем делать с переменной ТаблицаДляЗаписи любые серверные операции
    // (сортировать, индексировать, передавать в менеджер временных таблиц запроса)
    
КонецПроцедуры

Способ 3. Программное создание динамической таблицы на форме

Что делать, если структура таблицы заранее неизвестна (колонки определяются динамически во время выполнения кода на сервере), но ее обязательно нужно вывести пользователю на клиенте?

В этом случае мы можем прибегнуть к программному созданию колонок реквизита формы. Проанализируем, как это сделать правильно:


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

Способ 4. Передача через Временное Хранилище (Сценарий: Сервер - Клиент - Сервер)

Очень часто программисты совершают архитектурную ошибку: они передают таблицу значений на клиент только для того, чтобы в следующем действии (например, при нажатии другой кнопки) вернуть ее обратно на сервер для обработки.

Запомните правило: Если клиенту не нужно видеть данные таблицы на экране или менять их руками, саму таблицу на клиент передавать вообще не нужно!

Вместо этого используйте механизм Временного хранилища. На клиент возвращается только короткий уникальный строковый адрес, который весит несколько байт и передается мгновенно.

Рассмотрим этот эффективный паттерн по шагам:


&НаСервере
Функция СформироватьТаблицуИПоместитьВХранилище()
    
    ТЗ = СформироватьТяжелуюТаблицу(); // Функция, возвращающая ТаблицуЗначений
    
    // Помещаем таблицу во временное хранилище, привязывая её к уникальному идентификатору формы
    АдресХранилища = ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификатор);
    
    // Возвращаем на клиент только строку-адрес
    Возврат АдресХранилища;
    
КонецФункции

&НаКлиенте
Процедура ВыполнитьДействие()
    
    // Шаг 1: Формируем данные на сервере, получаем только адрес адресации
    АдресДанных = СформироватьТаблицуИПоместитьВХранилище();
    
    // Шаг 2: Клиент просто хранит эту строку. 
    // При необходимости мы передаем этот адрес в другой серверный метод.
    ОбработатьДанныеНаСервере(АдресДанных);
    
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ОбработатьДанныеНаСервере(Знач АдресДанных)
    
    // Шаг 3: На сервере извлекаем готовую таблицу значений и обрабатываем
    ТЗ = ПолучитьИзВременногоХранилища(АдресДанных);
    
    Если ТЗ <> Неопределено Тогда
        Для Каждого СтрокаТЗ Из ТЗ Цикл
            // Выполняем проведение документов, запись в регистры и т.д.
        КонецЦикла;
    КонецЕсли;
    
КонецПроцедуры

Способ 5. Сериализация в XML или JSON

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

Объект ТаблицаЗначений изначально поддерживает XDTO-сериализацию. Рассмотрим пример упаковки таблицы в XML-строку:


&НаСервере
Функция СериализоватьТЗвXML(ТЗ)
    
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    
    // Сериализуем объект таблицы значений со всей структурой и типами колонок
    СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ТЗ, НазначениеТипаXML.Явное);
    
    Возврат ЗаписьXML.Закрыть();
    
КонецФункции

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


&НаСервере
Функция ДесериализоватьТЗизXML(СтрокаXML)
    
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(СтрокаXML);
    
    ТЗ = СериализаторXDTO.ПрочитатьXML(ЧтениеXML, Тип("ТаблицаЗначений"));
    
    Возврат ТЗ;
    
КонецФункции

Начиная с версии 8.3.6, платформа также поддерживает сериализацию в формат JSON с помощью аналогичного метода СериализаторXDTO.ЗаписатьJSON(), что обеспечивает еще большую скорость работы и меньший размер передаваемой строки.


Развенчиваем мифы: Действительно ли реквизиты формы нагружают сервер?

В обсуждениях программистов часто встречается мнение (например, в сообщении 46 на форуме), что использование реквизитов формы вредно, так как платформа подтягивает данные порциями (по 45 строк), создавая постоянные микро-запросы к серверу при прокрутке.

Давайте проанализируем эту ситуацию с точки зрения производительности:

На самом деле, порционное считывание (ленивая загрузка, lazy loading) — это величайшее благо управляемых форм. Представьте, что ваш запрос возвращает 50 000 строк. Если вы преобразуете их в массив структур и передадите на клиент, браузер или тонкий клиент мгновенно «зависнет», пытаясь обработать и отрисовать такой гигантский массив данных в оперативной памяти компьютера.

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


Резюме: какую стратегию выбрать?

Для того чтобы не путаться в многообразии подходов, используйте простую шпаргалку при проектировании ваших механизмов в управляемом приложении:

← На главную