Таблица значений между клиентом и сервером: рабочие схемы с примерами кода
Содержание
Таблицу значений нельзя передать с клиента на сервер как есть: в тонком клиенте и веб-клиенте типа ТаблицаЗначений просто нет. Передают её данные. Клиент упаковывает строки в массив структур или хранит их в реквизите формы, а сервер собирает из них настоящую таблицу значений. Для больших объёмов клиент держит только адрес во временном хранилище.
Всё ниже относится к управляемому приложению на платформе 8.3 и одинаково работает в любой конфигурации: БП 3.0, ЗУП 3.1, УТ 11 или ваша собственная разработка.
Почему таблица значений не проходит через границу клиент — сервер
Клиентский и серверный код в управляемом приложении выполняются в разных контекстах. Всё, что уходит в вызов сервера и возвращается обратно, платформа сериализует. Передать можно только те типы, которые существуют на обеих сторонах.
ТаблицаЗначений доступна на сервере, в толстом клиенте и во внешнем соединении. В тонком клиенте и веб-клиенте её нет. Поэтому код вида Таблица = Новый ТаблицаЗначений; в процедуре с директивой &НаКлиенте не скомпилируется, а серверная функция, которая возвращает таблицу значений, упадёт при вызове с тонкого клиента
.
Что можно передавать между клиентом и сервером:
- 🔢 примитивы: Строка, Число, Дата, Булево, Неопределено;
- 🔗 ссылки на объекты базы (
СправочникСсылка,ДокументСсылкаи т. п.); - 📦 универсальные коллекции:
Массив,Структура,Соответствиеи их фиксированные варианты, если внутри них тоже допустимые типы; - 🧾 данные формы:
ДанныеФормыКоллекция,ДанныеФормыСтруктура— это клиентское «отражение» таблиц и объектов; - 🗂 строку-адрес во временном хранилище.
В толстом клиенте обычного приложения таблица значений существует и на клиенте. Если конфигурация должна работать и в тонком клиенте, на это не опирайтесь: пишите так, будто клиент тонкий.
Схема преобразования: что происходит с данными по пути
Любой способ передачи сводится к трём действиям:
- На клиенте данные приводят к допустимому типу: массив структур, коллекция формы или адрес в хранилище.
- Вызов сервера переносит этот «контейнер». Платформа копирует его, а не передаёт по ссылке.
- На сервере из контейнера заново создают
ТаблицаЗначений: добавляют колонки с типами и переносят строки.
Обратный путь зеркальный. Сервер превращает таблицу в массив структур, загружает её в реквизит формы или кладёт во временное хранилище.
| Способ | Что держит клиент | Когда подходит | Минусы |
|---|---|---|---|
| Массив структур | Массив из Структура | Сотни и тысячи строк, вызов без контекста | Типы колонок теряются, их задают заново на сервере |
| Реквизит формы | ДанныеФормыКоллекция | Таблица выводится на форму, пользователь её правит | Вызов &НаСервере тянет все данные формы |
| Временное хранилище | Строку-адрес | Большие таблицы, многократные вызовы | Нужно следить за временем жизни значения |
Клиентский код: упаковываем строки для отправки
Самый частый случай — на форме документа есть табличная часть Товары, и её строки нужно обработать на сервере без передачи всей формы.
&НаКлиенте
Процедура ОтправитьНаСервер(Команда)
Строки = Новый Массив;
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
ОписаниеСтроки = Новый Структура("Номенклатура, Количество, Цена",
СтрокаТЧ.Номенклатура, СтрокаТЧ.Количество, СтрокаТЧ.Цена);
Строки.Добавить(ОписаниеСтроки);
КонецЦикла;
Итог = ОбработатьСтрокиНаСервере(Строки);
Сообщить(СтрШаблон("Обработано строк: %1, сумма: %2",
Итог.Количество, Формат(Итог.Сумма, "ЧДЦ=2")));
КонецПроцедуры
Если таблиц несколько, удобно вынести упаковку в универсальную клиентскую функцию. Ей передают коллекцию и список колонок через запятую:
&НаКлиенте
Функция КоллекцияВМассив(Коллекция, ИменаКолонок)
Результат = Новый Массив;
Для Каждого Строка Из Коллекция Цикл
Элемент = Новый Структура(ИменаКолонок);
ЗаполнитьЗначенияСвойств(Элемент, Строка);
Результат.Добавить(Элемент);
КонецЦикла;
Возврат Результат;
КонецФункции
ЗаполнитьЗначенияСвойств заполняет только те ключи, которые уже есть в структуре. Лишние колонки табличной части в массив не попадут, и по сети уйдёт меньше данных.
Передавайте ссылки, а не представления. Строка «Гвозди 100 мм» на сервере бесполезна, а СправочникСсылка.Номенклатура передаётся без проблем и весит немного.
Серверный код: собираем таблицу значений заново
Из массива структур
На сервере таблицу создают с явными типами колонок. Без типов не будет работать передача таблицы в запрос и корректный подсчёт итогов.
&НаСервереБезКонтекста
Функция ОбработатьСтрокиНаСервере(Знач Строки)
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура",
Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Таблица.Колонки.Добавить("Количество",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Таблица.Колонки.Добавить("Цена",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
Таблица.Колонки.Добавить("Сумма",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
Для Каждого ОписаниеСтроки Из Строки Цикл
НоваяСтрока = Таблица.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ОписаниеСтроки);
НоваяСтрока.Сумма = НоваяСтрока.Количество * НоваяСтрока.Цена;
КонецЦикла;
Результат = Новый Структура;
Результат.Вставить("Количество", Таблица.Количество());
Результат.Вставить("Сумма", Таблица.Итог("Сумма"));
Возврат Результат;
КонецФункции
Пример: три строки 2 × 150, 5 × 40 и 1 × 1 000. Функция вернёт Количество = 3 и Сумма = 1 500 (300 + 200 + 1 000).
Обратите внимание на Знач у параметра. Изменения массива на сервере всё равно не вернутся на клиент, а Знач делает это явным для того, кто будет читать код.
Из реквизита формы
Если таблица живёт в реквизите формы, ничего упаковывать не нужно. Процедура с директивой &НаСервере видит данные формы, а методы формы превращают их в таблицу значений и обратно:
&НаСервере
Процедура СвернутьТаблицуНаСервере()
Таблица = РеквизитФормыВЗначение("ТаблицаДанных");
Таблица.Свернуть("Номенклатура", "Количество");
ЗначениеВРеквизитФормы(Таблица, "ТаблицаДанных");
КонецПроцедуры
Для табличной части объекта используйте методы коллекции формы. Они доступны только на сервере:
&НаСервере
Процедура ПересчитатьТоварыНаСервере()
Таблица = Объект.Товары.Выгрузить();
// ... обработка таблицы значений ...
Объект.Товары.Загрузить(Таблица);
КонецПроцедуры
После возврата с сервера платформа сама переносит изменённые данные формы на клиент. Это удобно, но об этом стоит помнить, когда таблица большая (об этом — в следующем разделе).
Большие таблицы: как не утонуть в вызовах сервера
Каждый вызов сервера — это сериализация, передача по сети и разбор на другой стороне. На локальной файловой базе разница почти незаметна. На сервере 1С через медленный канал или в веб-клиенте она становится главной причиной «зависаний» формы.
Что даёт реальный выигрыш:
- Один вызов вместо цикла вызовов. Обращение к серверу внутри
Для Каждогопо строкам — худший вариант. 5 000 строк дадут 5 000 обращений по сети. Соберите всё в один массив и вызовите сервер один раз. &НаСервереБезКонтекставместо&НаСервере. Вызов с контекстом передаёт все данные формы туда и обратно, включая таблицы, которые вы не трогали. Без контекста уходят только параметры.- Только нужные колонки. Если серверу нужны номенклатура и количество, не отправляйте десять колонок с характеристиками, комментариями и суммами.
- Только изменённые строки. При повторной отправке передавайте строки, которые пользователь правил, а не всю таблицу.
- Адрес вместо данных. Если таблица нужна серверу несколько раз за жизнь формы, положите её во временное хранилище один раз и передавайте адрес.
Схема с временным хранилищем выглядит так:
&НаСервереБезКонтекста
Функция ПодготовитьДанные(ИдентификаторФормы)
Запрос = Новый Запрос(
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура");
Таблица = Запрос.Выполнить().Выгрузить();
Возврат ПоместитьВоВременноеХранилище(Таблица, ИдентификаторФормы);
КонецФункции
&НаСервереБезКонтекста
Функция КоличествоСтрок(Адрес)
Таблица = ПолучитьИзВременногоХранилища(Адрес);
Возврат Таблица.Количество();
КонецФункции
На клиенте:
&НаКлиенте
Процедура ЗагрузитьДанные(Команда)
АдресТаблицы = ПодготовитьДанные(УникальныйИдентификатор);
Сообщить(СтрШаблон("Строк в таблице: %1", КоличествоСтрок(АдресТаблицы)));
КонецПроцедуры
АдресТаблицы — реквизит формы типа «Строка». Таблица значений ни разу не покидает сервер, по сети гуляет только короткая строка.
Время жизни значения зависит от второго параметра ПоместитьВоВременноеХранилище. С уникальным идентификатором формы значение хранится, пока форма открыта. Без него оно удаляется вскоре после завершения вызова сервера. В функции без контекста формы УникальныйИдентификатор недоступен, поэтому его передают параметром, как в примере.
💡 Когда данные больше не нужны, освободите память явно: УдалитьИзВременногоХранилища(АдресТаблицы) в обработчике закрытия формы. Для таблиц на десятки тысяч строк это заметно.
Какой способ выбрать и чем ещё можно обмениваться
Кроме трёх основных схем есть ещё несколько механизмов. Они нужны реже, но знать их полезно:
- 🧊 Фиксированные коллекции.
ФиксированныйМассивиФиксированнаяСтруктуразащищают переданные данные от случайного изменения. Удобно для настроек, которые сервер отдаёт клиенту только на чтение. - 🗺 Соответствие. Подходит, когда нужен быстрый поиск по ключу, например «номенклатура → остаток». Передаётся между клиентом и сервером так же, как структура.
- 🔤 Строка внутреннего формата.
ЗначениеВСтрокуВнутриЗначениеИзСтрокиВнутрработают на сервере. Таблицу можно превратить в строку и отдать клиенту, но это громоздко и проигрывает временному хранилищу. Оправдано, если строку нужно сохранить, например в реквизит объекта. - 📄 JSON. Объекты
ЗаписьJSONиЧтениеJSONесть и на клиенте, но напрямую пишут только примитивы, массивы и структуры. Для обмена внутри одной базы это лишний слой, для интеграций — нормальный формат. - ⏳ Фоновое задание. Если обработка таблицы долгая, сервер запускает фоновое задание, а результат кладёт во временное хранилище. Клиент периодически проверяет готовность по адресу и не блокирует пользователя.
Ошибки при передаче таблицы значений и что с ними делать
Ещё одна неочевидная ловушка — колонки с составным типом. Если в колонке могут лежать и ссылки, и числа, при сборке таблицы на сервере перечислите все типы в ОписаниеТипов через запятую: Новый ОписаниеТипов("Число, СправочникСсылка.Номенклатура"). Иначе значения не того типа приведутся к типу колонки, и вы получите нули или пустые ссылки без всякой ошибки.