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

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

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

Выясняем причину: понятие мутабельности и ограничения клиента

Для того чтобы понять суть проблемы, нам необходимо обратиться к архитектуре управляемой платформы 1С. Весь код в ней разделен на контексты. Сервер отвечает за непосредственную работу с базой данных, выполнение сложных вычислений и формирование тяжелых выборок. Клиент представляет собой «тонкую» оболочку, задача которой — отобразить интерфейс пользователю и обработать его интерактивные действия.

Платформа накладывает строгое табу на передачу так называемых мутабельных (изменяемых) типов данных через границу «клиент-сервер». К мутабельным объектам относятся:

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

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

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


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

Самым популярным и надежным решением является преобразование таблицы значений в массив структур. Массивы и структуры относятся к немутабельным типам данных — они легко подлежат сериализации и без проблем пересекают границу «клиент-сервер».

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

Ручной метод преобразования на сервере

Рассмотрим пример ручной конвертации таблицы значений на стороне сервера:


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

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


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

Использование готовых функций БСП (Библиотеки Стандартных Подсистем)

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

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

ОбщегоНазначения.ТаблицаЗначенийВМассив(ТаблицаЗначений)

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

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

ОбщегоНазначения.МассивВТаблицуЗначений(МассивСтруктур)


Способ 2. Использование реквизитов формы и типа ДанныеФормыКоллекция

Очень часто программисты путают понятия «Таблица значений в оперативной памяти» и «Таблица значений на форме управляемого приложения». На форме управляемого приложения таблица визуализируется с помощью специального типа данных — ДанныеФормыКоллекция (или ДанныеФормыДерево для иерархических структур).

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

Передача и изменение данных с помощью ЗначениеВРеквизитФормы

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


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

Этот подход невероятно удобен, так как избавляет программиста от написания рутинного кода по переносу значений из ячейки в ячейку. При вызове ЗначениеВРеквизитФормы платформа автоматически обновит видимые строки на клиенте.

Динамическое управление колонками таблицы формы

Некоторые разработчики полагают, что на сервере нельзя динамически менять структуру колонок у реквизита формы. Это заблуждение. Давайте разберем по шагам, как добавить колонку в таблицу формы «на лету»:


&НаСервере
Процедура ДобавитьКолонкуДинамическиНаСервере()
    
    // Шаг 1. Подготавливаем массив добавляемых реквизитов
    МассивДобавляемыхРеквизитов = Новый Массив;
    
    // Указываем Имя, Тип и Путь к родительскому элементу (нашей таблице на форме)
    НовыйРеквизит = Новый РеквизитФормы("ДополнительнаяСкидка", Новый ОписаниеТипов("Число", , , Новый КвалификаторыЧисла(5, 2)), "АдреснаяТаблицаФормы", "Скидка %");
    МассивДобавляемыхРеквизитов.Добавить(НовыйРеквизит);
    
    // Шаг 2. Вызываем метод изменения реквизитов формы
    ЭтаФорма.ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);
    
    // Шаг 3. Добавляем визуальный элемент (поле ввода) в элемент формы "ТаблицаФормы"
    ЭлементТаблица = Элементы.АдреснаяТаблицаФормы;
    НовоеПоле = Элементы.Добавить("АдреснаяТаблицаФормыДополнительнаяСкидка", Тип("ПолеФормы"), ЭлементТаблица);
    НовоеПоле.Вид = ВидПоляФормы.ПолеВвода;
    НовоеПоле.ПутьКДанным = "АдреснаяТаблицаФормы.ДополнительнаяСкидка";
    
КонецПроцедуры

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


Способ 3. Передача больших таблиц через Временное хранилище

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

Временное хранилище не стирает ограничения типов для клиента! Если вы поместите таблицу значений во временное хранилище на сервере, а затем попытаетесь вызвать метод ПолучитьИзВременногоХранилища() непосредственно на клиенте, платформа завершит работу аварийно. Клиент все так же не умеет работать с типом ТаблицаЗначений, даже если она «прилетела» из хранилища.

Для чего тогда нужно временное хранилище? Оно незаменимо при передаче данных между серверными вызовами без участия клиента или при передаче больших массивов информации между разными формами.

Давайте проанализируем правильный паттерн использования временного хранилища:


// Форма №1 (или серверный модуль)
&НаСервере
Функция ПоместитьТаблицуВХранилище()
    
    ТаблицаДляПередачи = СформироватьСложнуюТЗ(); // Тяжелая таблица значений
    
    // Помещаем таблицу во временное хранилище и получаем строковый адрес
    АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаДляПередачи, ЭтаФорма.УникальныйИдентификатор);
    
    // Адрес — это обычная строка. Мы безопасно возвращаем его на клиент!
    Возврат АдресХранилища;
    
КонецФункции

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

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


Способ 4. Сериализация в JSON (Продвинутый метод для больших объемов данных)

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

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

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


&НаСервере
Функция ПолучитьТЗвФорматеJSON()
    
    Таблица = ПолучитьИсходныеДанные(); // Наша таблица значений
    
    // Для сериализации переведем ее в массив структур через БСП
    МассивДанных = ОбщегоНазначения.ТаблицаЗначенийВМассив(Таблица);
    
    // Записываем массив структур в формат JSON
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, МассивДанных);
    СтрокаJSON = ЗаписьJSON.Закрыть();
    
    Возврат СтрокаJSON;
    
КонецФункции

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

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


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

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

  1. Массив структур (ручной или БСП)
    • Плюсы: Простота реализации, поддержка ссылочных типов 1С, стандартное поведение системы.
    • Минусы: Может снижать скорость работы при объемах данных более 10 000 строк.
    • Рекомендуется для: Большинства повседневных задач, передачи небольших и средних таблиц.
  2. Реквизит формы (ДанныеФормыКоллекция)
    • Плюсы: Платформа сама синхронизирует изменения, легкое отображение в интерфейсе, поддержка динамических колонок.
    • Минусы: Данные "привязаны" к конкретной форме, оверхед на передачу контекста формы целиком.
    • Рекомендуется для: Вывода таблиц на экран пользователю и интерактивного редактирования.
  3. Временное хранилище (Адрес ссылки)
    • Плюсы: Минимальный трафик между клиентом и сервером (передается только строка адреса), данные не нагружают память клиента.
    • Минусы: Данные невозможно прочитать непосредственно на клиенте.
    • Рекомендуется для: Передачи таблиц между формами, работы с фоновыми заданиями.
  4. Сериализация в JSON
    • Плюсы: Высокая скорость упаковки и распаковки, экономия трафика при больших объемах строк.
    • Минусы: Трудности с передачей ссылочных типов данных (требуется ручная обработка UUID).
    • Рекомендуется для: Передачи больших массивов простой текстовой и числовой информации.

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

← На главную