Как правильно передать данные из Таблицы Значений на клиент в 1С:Предприятие?

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

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

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

Давайте вместе разберем эту проблему по шагам и рассмотрим все доступные инструменты.

Почему Таблица Значений не живет на клиенте? Понятие мутабельности

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

Объект ТаблицаЗначений является так называемым мутабельным (изменяемым) типом данных. Он содержит не просто строки и колонки, а сложные механизмы индексации, поиска, вычисления итогов и управления типами колонок. Передача такого тяжелого объекта между сервером и клиентом потребовала бы огромных ресурсов для его сериализации (преобразования в XML/XDTO-пакет для передачи по сети) и десериализации на клиенте. Кроме того, синхронизация изменений в мутабельных объектах между клиентом и сервером перегрузила бы сетевой трафик.

Поэтому платформа разрешает передавать через границу "Клиент-Сервер" только примитивные типы данных (числа, строки, даты, булево), ссылки на объекты базы данных (поскольку ссылка — это по сути простой уникальный идентификатор GUID) и специальные немутабельные коллекции (например, Массив, Структура, Соответствие).

Теперь, когда мы понимаем теоретическую базу, рассмотрим практические способы передачи данных на клиент.

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

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

Давайте проанализируем пример кода, предложенный в сообщении 10 на форуме:


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

Внимание: данный код крайне неоптимален! Если в вашей таблице значений будет несколько тысяч строк, этот алгоритм покажет очень низкую скорость работы. Проблема кроется во вложенном цикле Для Каждого ИмяКолонки Из тзДанные.Колонки. На каждой итерации основного цикла платформа тратит драгоценное время на обращение к метаданным колонок таблицы значений на встроенном языке.

Рассмотрим, как оптимизировать этот процесс и сделать его максимально быстрым с помощью метода "быстрой структуры". Вместо динамического добавления свойств в структуру мы будем создавать структуру с заранее определенным списком ключей, а для копирования значений используем быструю встроенную функцию платформы ЗаполнитьЗначенияСвойств:


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

Этот алгоритм работает в разы быстрее, так как функция ЗаполнитьЗначенияСвойств реализована на уровне ядра платформы (на языке C++) и отрабатывает мгновенно даже на больших объемах данных.

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


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

Способ 2. Использование Реквизитов Формы (Связывание Данных)

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

На клиенте этот реквизит принимает тип ДанныеФормыКоллекция. Данный тип доступен на клиенте, его можно обходить в цикле, интерактивно изменять и связывать с элементом управления "Таблица" на форме.

Для быстрой конвертации платформенного объекта ТаблицаЗначений в реквизит формы на сервере используются штатные методы:

  1. ЗначениеВРеквизитФормы(МояТЗ, "ИмяРеквизитаФормы") — заполняет реквизит формы данными из серверного объекта.
  2. РеквизитФормыВЗначение("ИмяРеквизитаФормы") — преобразует данные реквизита обратно в полноценную серверную ТаблицуЗначений (например, для передачи в запрос или записи в базу данных).

Посмотрим на практический пример использования этого механизма:


&НаКлиенте
Процедура ЗагрузитьДанныеНаФорму()
    // Вызываем серверный метод для заполнения данных
    ЗаполнитьТаблицуНаСервере();
КонецПроцедуры

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

Этот способ является наиболее производительным для интерактивных задач, так как конвертация выполняется механизмами самой платформы без написания циклов на встроенном языке.

Способ 3. Передача простых типов (Массивы и Строки)

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

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


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

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

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


&НаСервере
Функция ПолучитьСтрокуИНН()
    // Получаем массив ИНН
    МассивИНН = ПолучитьМассивИНН();
    // Объединяем элементы массива в одну строку
    Возврат СтрСоединить(МассивИНН, ", ");
КонецФункции

Способ 4. Сериализация данных в формат JSON

В современных версиях платформы 1С:Предприятие (начиная с 8.3.10 и выше) реализована мощная встроенная поддержка работы с форматом JSON. Этот способ незаменим, когда данные необходимо передать не просто в клиентскую процедуру, а, например, передать во встроенное HTML-поле (для обработки скриптами на JavaScript) или отправить через внешние веб-сервисы.

Вы можете сериализовать таблицу значений в строку JSON на сервере, используя объект СериализаторXDTO, передать эту строку на клиент и там разобрать ее обратно в массив структур с помощью стандартного объекта ЧтениеJSON.

Рассмотрим, как это выглядит в коде:


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

&НаКлиенте
Процедура ОбработатьJSONНаКлиенте()
    // Получаем JSON-строку с сервера
    СтрокаJSON = ПолучитьJSONТаблицы();
    
    // Читаем строку JSON в массив структур на клиенте
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
    
    МассивДанных = ПрочитатьJSON(ЧтениеJSON);
    ЧтениеJSON.Закрыть();
    
    // Теперь МассивДанных содержит массив структур, готовый к работе
КонецПроцедуры

Разбор типичных ошибок: Подвох со Временным Хранилищем

Часто начинающие разработчики, прочитав рекомендации в интернете, пытаются решить проблему "в лоб" через временное хранилище:

  1. На сервере помещают ТаблицуЗначений во временное хранилище с помощью функции ПоместитьВоВременноеХранилище(МояТЗ, УникальныйИдентификатор).
  2. Передают полученный адрес (строку вида "e1cib/tempstorage/...") на клиент.
  3. На клиенте вызывают ПолучитьИзВременногоХранилища(Адрес).

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

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

Сравнительный анализ методов

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

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

← На главную