Строка табличной части в управляемой форме: от клиента к серверу и обратно

· 9 мин

Содержание

Передать строку табличной части с клиента на сервер в 1С можно двумя надёжными способами. Первый: передать идентификатор строки (Элементы.Товары.ТекущаяСтрока) в процедуру &НаСервере и найти её там методом Объект.Товары.НайтиПоИдентификатору(). Второй: скопировать нужные поля строки в Структура через ЗаполнитьЗначенияСвойств() и отправить её в функцию &НаСервереБезКонтекста, а результат вернуть обратно в строку.

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

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

Почему строку нельзя просто «увидеть» на сервере

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

Табличная часть документа на форме — это не ТабличнаяЧасть объекта, а ДанныеФормыКоллекция. Каждая её строка — ДанныеФормыЭлементКоллекции. У каждой строки есть внутренний идентификатор: число, которое платформа присваивает строке при её появлении в коллекции.

Из этого следуют три правила:

  • 🧭 Строку на сервере ищут по идентификатору, а не по номеру. НомерСтроки меняется при сортировке и удалении, идентификатор — нет.
  • 📦 На сервер уходит либо вся форма, либо только параметры. Это определяет директива компиляции метода.
  • 🔁 Изменения возвращаются по-разному. При вызове с контекстом правки в Объект.Товары сами попадают на форму. Без контекста — только то, что вы вернули явно.

Метод модуля формы без директивы компилируется как &НаСервере. Если забыли поставить директиву, вызов будет контекстным — со всеми данными формы.

Директивы и способы вызвать сервер из формы

Выбор способа передачи строки начинается с выбора директивы. От неё зависят объём данных в вызове и доступ к реквизитам формы.

СпособЧто уходит на серверДоступ к Объект и реквизитамКогда брать
&НаСервереданные формы целиком плюс параметрыестьнужно изменить саму табличную часть, несколько строк, итоги
&НаСервереБезКонтекстатолько параметрынетрасчёт по одной строке: цена, остаток, реквизиты номенклатуры
Общий модуль с флагами «Сервер» и «Вызов сервера»только параметрынетодна и та же логика нужна в нескольких формах
Общий модуль с повторным использованием возвращаемых значенийтолько параметры, результат кешируетсянетсправочные данные, которые не меняются за сеанс

Контекстный вызов удобен, но тяжёл. Если в документе 2 000 строк товаров, при каждом таком вызове платформа переносит данные формы на сервер и обратно. Для пересчёта одной строки это лишняя нагрузка.

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

Что подготовить на клиенте перед передачей строки

В параметрах серверного вызова допустимы значения, которые платформа умеет сериализовать: примитивные типы (Число, Строка, Дата, Булево), ссылки на объекты базы, Структура, Массив, Соответствие и их фиксированные варианты. Элементы управления формы и объекты данных формы в этот список не входят.

Подготовка зависит от выбранного способа:

  1. Для &НаСервере достаточно идентификатора: Элементы.Товары.ТекущаяСтрока. Для нескольких строк — массив Элементы.Товары.ВыделенныеСтроки.
  2. Для &НаСервереБезКонтекста создайте Структура с ключами — именами нужных колонок — и заполните её из Элементы.Товары.ТекущиеДанные.
  3. Проверьте, что строка выбрана. Если курсор не стоит на строке, ТекущиеДанные и ТекущаяСтрока равны Неопределено.

ЗаполнитьЗначенияСвойств() копирует только те свойства, которые уже есть в приёмнике. Поэтому ключи структуры перечисляют заранее, в конструкторе. Пустая Новый Структура после копирования так и останется пустой.

Кладите в структуру только колонки, нужные для расчёта. Пять полей вместо двадцати — меньше данных в каждом вызове и меньше риск случайно перезаписать лишнее при возврате.

Передача строки табличной части на сервер: два рабочих варианта

Оба примера рассчитаны на документ с табличной частью Товары и колонками Номенклатура, Количество, Цена, Сумма. Имена замените на свои.

Вариант 1. Идентификатор строки и вызов с контекстом

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

&НаКлиенте
Процедура ПересчитатьСтроку(Команда)

	ИдентификаторСтроки = Элементы.Товары.ТекущаяСтрока;
	Если ИдентификаторСтроки = Неопределено Тогда
		Возврат;
	КонецЕсли;

	ПересчитатьСтрокуНаСервере(ИдентификаторСтроки);

КонецПроцедуры

&НаСервере
Процедура ПересчитатьСтрокуНаСервере(Знач ИдентификаторСтроки)

	СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(ИдентификаторСтроки);
	Если СтрокаТЧ = Неопределено Тогда
		Возврат;
	КонецЕсли;

	СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;

КонецПроцедуры

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

Вариант 2. Структура и вызов без контекста

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

&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)

	ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;

	ДанныеСтроки = Новый Структура("Номенклатура, Количество, Цена, Сумма");
	ЗаполнитьЗначенияСвойств(ДанныеСтроки, ТекущиеДанные);

	Результат = ЗаполнитьЦенуСтроки(ДанныеСтроки, Объект.Дата);
	ЗаполнитьЗначенияСвойств(ТекущиеДанные, Результат);

КонецПроцедуры

&НаСервереБезКонтекста
Функция ЗаполнитьЦенуСтроки(Знач ДанныеСтроки, Знач ДатаЦены)

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Цены.Цена КАК Цена
	|ИЗ
	|	РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, Номенклатура = &Номенклатура) КАК Цены";
	Запрос.УстановитьПараметр("Дата", ДатаЦены);
	Запрос.УстановитьПараметр("Номенклатура", ДанныеСтроки.Номенклатура);

	Выборка = Запрос.Выполнить().Выбрать();
	ДанныеСтроки.Цена = ?(Выборка.Следующий(), Выборка.Цена, 0);
	ДанныеСтроки.Сумма = ДанныеСтроки.Количество * ДанныеСтроки.Цена;

	Возврат ДанныеСтроки;

КонецФункции

Регистр цен в типовых конфигурациях устроен по-разному: где-то есть измерение вида или типа цен, где-то цены хранятся в другом регистре. Условие среза подстройте под свою конфигурацию.

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

Обработка строки на сервере и возврат результата

Сервер получил строку — дальше важно правильно отдать результат клиенту.

Параметры с «Знач» и без

Параметр без Знач при серверном вызове передаётся туда и обратно: если сервер его изменил, клиент получит изменённое значение. Параметр со Знач идёт только на сервер. Для данных строки удобнее явный возврат функцией и Знач в параметрах — видно, что именно вернулось, и данные не гоняются лишний раз.

Возврат на форму

  • ✅ При вызове с контекстом правьте Объект.Товары прямо на сервере — изменения придут на форму сами.
  • ✅ При вызове без контекста возвращайте Структура и копируйте её в строку через ЗаполнитьЗначенияСвойств(ТекущиеДанные, Результат).
  • ✅ Если сервер должен добавить строку, при вызове с контекстом используйте Объект.Товары.Добавить(). Без контекста верните данные новой строки, а добавьте её на клиенте.

Новая строка с сервера и курсор на ней

Частая задача: сервер добавляет строку, а пользователь должен сразу оказаться на ней. Верните идентификатор новой строки и установите его в ТекущаяСтрока на клиенте.

&НаКлиенте
Процедура ДобавитьУслугуДоставки(Команда)

	ИдентификаторНовой = ДобавитьСтрокуНаСервере();
	Элементы.Товары.ТекущаяСтрока = ИдентификаторНовой;

КонецПроцедуры

&НаСервере
Функция ДобавитьСтрокуНаСервере()

	НоваяСтрока = Объект.Товары.Добавить();
	НоваяСтрока.Количество = 1;

	Возврат НоваяСтрока.ПолучитьИдентификатор();

КонецФункции

Номенклатуру доставки и цену заполните по правилам своей конфигурации. Возвращать саму строку бессмысленно: на клиент она всё равно придёт вместе с данными формы, а нужен только её идентификатор.

Несколько выделенных строк

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

&НаКлиенте
Процедура ОбнулитьСкидкуВыделенных(Команда)

	Выделенные = Элементы.Товары.ВыделенныеСтроки;
	Если Выделенные.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;

	ОбнулитьСкидкуНаСервере(Выделенные);

КонецПроцедуры

&НаСервере
Процедура ОбнулитьСкидкуНаСервере(Знач Идентификаторы)

	Для Каждого Идентификатор Из Идентификаторы Цикл
		СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
		Если СтрокаТЧ <> Неопределено Тогда
			СтрокаТЧ.Скидка = 0;
			СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
		КонецЕсли;
	КонецЦикла;

КонецПроцедуры

Колонка Скидка здесь для примера — подставьте свою.

Большие табличные части: как не тормозить форму

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

Один вызов вместо цикла вызовов

Самая частая причина тормозов — серверная функция внутри клиентского цикла по строкам. 500 строк — 500 обращений к серверу, каждое со своей сетевой задержкой. Соберите данные всех строк в Массив структур или массив идентификаторов и обработайте их на сервере за один вызов, одним запросом с параметром-списком.

Таблица значений на сервере

Внутри метода &НаСервере табличную часть формы можно превратить в ТаблицаЗначений: Объект.Товары.Выгрузить(). С таблицей значений удобно группировать, сворачивать и передавать её в запрос как параметр. Обратно — Объект.Товары.Загрузить(Таблица).

Если нужен прикладной объект целиком — с его методами из модуля объекта, — используйте РеквизитФормыВЗначение("Объект"), а после изменений ЗначениеВРеквизитФормы(ДокументОбъект, "Объект").

Временное хранилище

Когда сервер готовит большой набор данных, а клиенту нужен только доступ к нему позже, положите результат в ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор) и передайте на клиент адрес — строку. Тот же адрес клиент потом отдаёт другому серверному вызову или фоновому заданию, а данные достают через ПолучитьИзВременногоХранилища(Адрес). Второй параметр — уникальный идентификатор формы: данные живут, пока открыта форма.

Как найти лишние вызовы

Запустите форму в отладке и включите в Конфигураторе Отладка → Замер производительности. В результатах замера видно, какие строки модуля выполнялись чаще всего и сколько времени заняли. Строки с вызовом серверной функции в клиентском цикле сразу всплывают наверх.

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

Ошибки при передаче строк ТЧ и что с ними делать

Ниже симптомы, которые встречаются при отладке такого кода, и их причины.

Диагностика: выберите симптом

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

Частые вопросы

Можно ли передать на сервер всю табличную часть, а не одну строку?
Да. В методе &НаСервере табличная часть уже доступна как Объект.Товары. Для вызова без контекста соберите строки в массив структур на клиенте.
Нужно ли записывать документ, чтобы сервер увидел новую строку?
Нет. При вызове с контекстом сервер работает с данными формы, включая строки, которые ещё не записаны в базу.
Чем `ТекущаяСтрока` отличается от `ТекущиеДанные`?
ТекущаяСтрока — идентификатор строки, число. Его можно передать на сервер. ТекущиеДанные — сама строка данных формы, она нужна для чтения и записи полей на клиенте.
Как передать строку табличной части в фоновое задание?
Так же, как в вызов без контекста: в параметры задания кладите структуру с простыми значениями и ссылками или адрес временного хранилища.
Работает ли этот подход в веб-клиенте?
Да. Код в управляемых формах одинаково выполняется в тонком клиенте, веб-клиенте и толстом клиенте управляемого приложения. В веб-клиенте лишние серверные вызовы заметнее из-за сетевой задержки.