Разработка и Конфигурация · 11 мин
Как передать строку табличной части с клиента на сервер в 1С
Разбираем способы передачи строки табличной части с клиента на сервер в 1С 8.3: через ДанныеФормыКоллекция, структуру и ЗначениеВСтрокуВнутр. Примеры кода и ошибки.
Задача передать строку табличной части с клиента на сервер возникает в управляемых формах 1С постоянно: серверная процедура не видит контекст формы напрямую, а обработать данные строки нужно именно на сервере — например, запросом к базе или через менеджер объекта. Проблема в том, что напрямую передать «строку» нельзя: тип ДанныеФормыЭлементКоллекции не сериализуется между клиентом и сервером.
Попытка объявить серверную процедуру с параметром типа «строка табличной части» приведёт к ошибке вида «Ошибка передачи данных между клиентом и сервером. Значение недопустимого типа». Платформа просто не умеет упаковать этот объект для передачи. Поэтому применяются обходные приёмы: передача структуры с реквизитами строки, передача всей коллекции через ДанныеФормыВЗначение на сервере или выгрузка таблицы значений.
Выбор способа зависит от объёма данных и задачи. Если нужна одна строка — достаточно структуры. Если серверная процедура должна работать с реквизитом объекта целиком (например, пересчитать документ), правильнее передать ссылку на объект или работать с реквизитом формы на сервере через контекстный вызов.
Почему строку нельзя передать напрямую
В управляемой форме табличная часть хранится как реквизит формы типа ДанныеФормыКоллекция, а её строка — ДанныеФормыЭлементКоллекции. Эти типы существуют только внутри формы и привязаны к её контексту. При вызове серверной процедуры без контекста формы передаваемые параметры сериализуются, и платформа отклоняет любые несериализуемые значения.
То же ограничение касается и обратного направления: серверная функция не может вернуть строку табличной части. Вернуть можно структуру, массив, таблицу значений (в пределах разумного объёма) или строку внутреннего представления через ЗначениеВСтрокуВнутр.
Способ 1: передать строку как структуру
Самый универсальный приём — на клиенте собрать структуру, где ключи совпадают с именами колонок табличной части, и передать её в серверную процедуру. Структура сериализуется без ограничений.
&НаКлиенте
Процедура ТоварыПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
ДанныеСтроки = Новый Структура;
ДанныеСтроки.Вставить("Номенклатура", ТекущаяСтрока.Номенклатура);
ДанныеСтроки.Вставить("Количество", ТекущаяСтрока.Количество);
ДанныеСтроки.Вставить("Цена", ТекущаяСтрока.Цена);
Результат = ПересчитатьНаСервере(ДанныеСтроки);
ЗаполнитьЗначенияСвойств(ТекущаяСтрока, Результат);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПересчитатьНаСервере(ДанныеСтроки)
Результат = Новый Структура("Сумма, СтавкаНДС");
Результат.Сумма = ДанныеСтроки.Количество * ДанныеСтроки.Цена;
Результат.СтавкаНДС = ПолучитьСтавкуНДС(ДанныеСтроки.Номенклатура);
Возврат Результат;
КонецФункции
Обратите внимание на обратный путь: серверная функция возвращает структуру с рассчитанными значениями, а клиентская процедура переносит их в строку через ЗаполнитьЗначенияСвойств. Это стандартный паттерн «структура туда — структура обратно».
Ограничение способа — ручное перечисление колонок. Если колонок много или их состав меняется, код придётся поддерживать. Для типовых сценариев с 3–7 полями это оптимальный вариант по скорости и читаемости.
- 1Получите текущие данные строки через Элементы.ИмяТаблицы.ТекущиеДанные
- 2Соберите структуру с нужными колонками
- 3Вызовите серверную функцию, передав структуру параметром
- 4Верните из функции структуру с результатом
- 5Перенесите результат в строку через ЗаполнитьЗначенияСвойств
Способ 2: контекстный серверный вызов формы
Если серверная логика должна работать с табличной частью как с частью объекта (например, пересчитать весь документ), используйте процедуру с директивой &НаСервере (без «БезКонтекста»). В ней доступен реквизит формы, и табличную часть можно преобразовать в таблицу значений:
&НаКлиенте
Процедура КомандаПересчитать(Команда)
ПересчитатьНаСервере(Элементы.Товары.ТекущаяСтрока);
КонецПроцедуры
&НаСервере
Процедура ПересчитатьНаСервере(ИдентификаторСтроки)
ДокументОбъект = РеквизитФормыВЗначение("Объект");
СтрокаТовары = ДокументОбъект.Товары.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаТовары <> Неопределено Тогда
СтрокаТовары.Сумма = СтрокаТовары.Количество * СтрокаТовары.Цена;
КонецЕсли;
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Здесь с клиента передаётся не сама строка, а её идентификатор — целое число, которое сериализуется без проблем. На сервере по идентификатору строка находится уже в объекте. Методы РеквизитФормыВЗначение и ЗначениеВРеквизитФормы — дорогие: они копируют весь объект. Для документа с сотнями строк такой вызов ощутимо тяжелее передачи структуры.
Сравнение способов передачи
Быстро и дёшево, подходит для пересчёта одной строки. Минус — колонки перечисляются вручную.
Подходит, когда нужен весь объект на сервере. Минус — дорогие РеквизитФормыВЗначение/ЗначениеВРеквизитФормы.
Подходит для пакетной обработки нескольких строк. Минус — передаётся больше данных, чем нужно для одной строки.
Универсально, но строка нечитаема и зависит от версии платформы. Использовать как крайний вариант.
Способ 3: передать таблицу значений
Когда обрабатывать нужно не одну строку, а несколько (например, выделенные строки), на сервер передают таблицу значений. Таблица значений сериализуется полностью:
&НаКлиенте
Процедура ОбработатьВыделенные(Команда)
МассивСтрок = Новый Массив;
Для Каждого Ид Из Элементы.Товары.ВыделенныеСтроки Цикл
СтрокаДанных = Объект.Товары.НайтиПоИдентификатору(Ид);
Стр = Новый Структура("Номенклатура,Количество");
ЗаполнитьЗначенияСвойств(Стр, СтрокаДанных);
МассивСтрок.Добавить(Стр);
КонецЦикла;
ОбработатьНаСервере(МассивСтрок);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОбработатьНаСервере(МассивСтрок)
Для Каждого Стр Из МассивСтрок Цикл
// серверная обработка каждой строки
КонецЦикла;
КонецПроцедуры
Массив структур часто удобнее таблицы значений: не нужно описывать колонки и типы. Таблица значений оправдана, когда на сервере по ней строится запрос с временной таблицей.
Способ 4: ЗначениеВСтрокуВнутр
Для передачи произвольных сложных данных иногда применяют ЗначениеВСтрокуВнутр на клиенте и ЗначениеИзСтрокиВнутр на сервере. Приём рабочий, но у него есть минусы: внутреннее представление зависит от версии платформы, его нельзя отладить «глазами», и оно создаёт скрытую связность кода. Используйте этот способ только когда структура и таблица значений по какой-то причине не подходят.
Частые ошибки и как их проверить
Самая частая ошибка — попытка передать Элементы.Товары.ТекущиеДанные напрямую параметром серверной процедуры. Вторая по частоте — перепутанные директивы: процедура объявлена &НаСервереБезКонтекста, но внутри обращается к Объект или РеквизитФормыВЗначение, что приводит к ошибке компиляции или выполнения.
Ещё одна типичная проблема — после серверного вызова строка на клиенте «не обновилась». Причина: серверная процедура изменила свою копию данных, но результат не был возвращён и не перенесён в строку. Всегда проверяйте обратный путь данных.
Как проверить результат
Нет ошибки сериализации, параметры доходят
Ошибка 'Значение недопустимого типа'
В отладчике структура содержит все нужные поля с верными значениями
Поля пустые или отсутствуют в структуре
Рассчитанные поля строки обновились в форме
Строка осталась без изменений
Вызов одной строки выполняется без заметной паузы
Заметная задержка при каждом изменении строки
Что выбрать в типовых сценариях
Для пересчёта суммы в строке при изменении количества или цены лучше всего подходит структура: минимум трафика, простой код. Для операций над документом целиком (заполнение по основанию, групповой пересчёт) — контекстный серверный вызов с РеквизитФормыВЗначение. Для обработки выделенных строк — массив структур или таблица значений.
Если серверная логика не требует данных формы вообще (например, нужно только получить ставку НДС по номенклатуре), передавайте на сервер минимальный набор — одну ссылку. Чем меньше данные, пересекающие границу клиент–сервер, тем быстрее и надёжнее форма.
Что важно запомнить
Строка табличной части не сериализуется и не передаётся на сервер напрямую. Передавайте структуру с полями строки, идентификатор строки для контекстного вызова или массив структур для пакетной обработки — и всегда проверяйте обратный перенос результата в строку.
Частые вопросы
Можно ли передать строку табличной части в функцию &НаСервереБезКонтекста?
Нет, тип ДанныеФормыЭлементКоллекции не сериализуется. Соберите из строки структуру с нужными полями и передайте её — это стандартное решение.
Чем отличается &НаСервере от &НаСервереБезКонтекста при работе с табличной частью?
В процедуре &НаСервере доступны реквизиты формы, включая табличную часть объекта, и работают РеквизитФормыВЗначение/ЗначениеВРеквизитФормы. В процедуре без контекста формы недоступна — туда данные нужно передавать параметрами.
Как передать на сервер сразу несколько выделенных строк?
Пройдите циклом по Элементы.Таблица.ВыделенныеСтроки, получите каждую строку через НайтиПоИдентификатору, соберите массив структур и передайте его параметром. На сервере обработайте массив в цикле.
Почему после серверного вызова строка не изменилась?
Серверная процедура работает с копией данных. Нужно вернуть результат (структуру) и на клиенте перенести его в строку через ЗаполнитьЗначенияСвойств, либо в контекстном вызове выполнить ЗначениеВРеквизитФормы.
Можно ли передать всю табличную часть на сервер одним вызовом?
Да: в контекстной серверной процедуре она доступна как реквизит формы, либо можно собрать массив структур. Но передавать всю коллекцию ради одной строки нерационально — это лишний трафик между клиентом и сервером.
Работает ли Выгрузить() у табличной части на тонком клиенте?
На тонком клиенте выгрузка всей ДанныеФормыКоллекция в таблицу значений ограничена. Надёжный кросс-платформенный вариант — цикл по строкам со сбором массива структур.