Строка табличной части в управляемой форме: от клиента к серверу и обратно
Содержание
Передать строку табличной части с клиента на сервер в 1С можно двумя надёжными способами. Первый: передать идентификатор строки (Элементы.Товары.ТекущаяСтрока) в процедуру &НаСервере и найти её там методом Объект.Товары.НайтиПоИдентификатору(). Второй: скопировать нужные поля строки в Структура через ЗаполнитьЗначенияСвойств() и отправить её в функцию &НаСервереБезКонтекста, а результат вернуть обратно в строку.
Сам объект ТекущиеДанные в серверный метод как параметр не отправляйте. Это элемент коллекции данных формы, он живёт на клиенте и привязан к таблице формы. Всё, что нужно серверу, передавайте простыми значениями, ссылками или коллекциями вроде Структура и Массив.
Речь об управляемых формах платформы 8.3 — тонкий клиент, веб-клиент, толстый клиент в режиме управляемого приложения. В обычных формах код формы выполняется на клиенте вместе с объектом, и такой задачи там не возникает.
Почему строку нельзя просто «увидеть» на сервере
В управляемом приложении форма существует в двух местах. На клиенте — её данные и элементы. На сервере — тот же набор данных, но только на время серверного вызова. Между вызовами сервер ничего о форме не помнит.
Табличная часть документа на форме — это не ТабличнаяЧасть объекта, а ДанныеФормыКоллекция. Каждая её строка — ДанныеФормыЭлементКоллекции. У каждой строки есть внутренний идентификатор: число, которое платформа присваивает строке при её появлении в коллекции.
Из этого следуют три правила:
- 🧭 Строку на сервере ищут по идентификатору, а не по номеру.
НомерСтрокименяется при сортировке и удалении, идентификатор — нет. - 📦 На сервер уходит либо вся форма, либо только параметры. Это определяет директива компиляции метода.
- 🔁 Изменения возвращаются по-разному. При вызове с контекстом правки в
Объект.Товарысами попадают на форму. Без контекста — только то, что вы вернули явно.
Метод модуля формы без директивы компилируется как &НаСервере. Если забыли поставить директиву, вызов будет контекстным — со всеми данными формы.
Директивы и способы вызвать сервер из формы
Выбор способа передачи строки начинается с выбора директивы. От неё зависят объём данных в вызове и доступ к реквизитам формы.
| Способ | Что уходит на сервер | Доступ к Объект и реквизитам | Когда брать |
|---|---|---|---|
&НаСервере | данные формы целиком плюс параметры | есть | нужно изменить саму табличную часть, несколько строк, итоги |
&НаСервереБезКонтекста | только параметры | нет | расчёт по одной строке: цена, остаток, реквизиты номенклатуры |
| Общий модуль с флагами «Сервер» и «Вызов сервера» | только параметры | нет | одна и та же логика нужна в нескольких формах |
| Общий модуль с повторным использованием возвращаемых значений | только параметры, результат кешируется | нет | справочные данные, которые не меняются за сеанс |
Контекстный вызов удобен, но тяжёл. Если в документе 2 000 строк товаров, при каждом таком вызове платформа переносит данные формы на сервер и обратно. Для пересчёта одной строки это лишняя нагрузка.
Вызов без контекста лёгкий, но сервер получает только то, что вы положили в параметры. Обратиться к Объект, Элементы или реквизитам формы из такого метода нельзя — код не скомпилируется.
Что подготовить на клиенте перед передачей строки
В параметрах серверного вызова допустимы значения, которые платформа умеет сериализовать: примитивные типы (Число, Строка, Дата, Булево), ссылки на объекты базы, Структура, Массив, Соответствие и их фиксированные варианты. Элементы управления формы и объекты данных формы в этот список не входят.
Подготовка зависит от выбранного способа:
- Для
&НаСервередостаточно идентификатора:Элементы.Товары.ТекущаяСтрока. Для нескольких строк — массивЭлементы.Товары.ВыделенныеСтроки. - Для
&НаСервереБезКонтекстасоздайтеСтруктурас ключами — именами нужных колонок — и заполните её изЭлементы.Товары.ТекущиеДанные. - Проверьте, что строка выбрана. Если курсор не стоит на строке,
ТекущиеДанныеиТекущаяСтрокаравныНеопределено.
ЗаполнитьЗначенияСвойств() копирует только те свойства, которые уже есть в приёмнике. Поэтому ключи структуры перечисляют заранее, в конструкторе. Пустая Новый Структура после копирования так и останется пустой.
Кладите в структуру только колонки, нужные для расчёта. Пять полей вместо двадцати — меньше данных в каждом вызове и меньше риск случайно перезаписать лишнее при возврате.
Передача строки табличной части на сервер: два рабочих варианта
Оба примера рассчитаны на документ с табличной частью Товары и колонками Номенклатура, Количество, Цена, Сумма. Имена замените на свои.
Вариант 1. Идентификатор строки и вызов с контекстом
Подходит, когда сервер должен работать с данными формы: пересчитать строку и итоги, заполнить связанные колонки, проверить соседние строки.
&НаКлиенте
Процедура ПересчитатьСтроку(Команда)
ИдентификаторСтроки = Элементы.Товары.ТекущаяСтрока;
Если ИдентификаторСтроки = Неопределено Тогда
Возврат;
КонецЕсли;
ПересчитатьСтрокуНаСервере(ИдентификаторСтроки);
КонецПроцедуры
&НаСервере
Процедура ПересчитатьСтрокуНаСервере(Знач ИдентификаторСтроки)
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(ИдентификаторСтроки);
Если СтрокаТЧ = Неопределено Тогда
Возврат;
КонецЕсли;
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецПроцедуры
После возврата на клиент новая сумма сразу видна в таблице. Возвращать ничего не нужно: изменённые данные формы платформа переносит обратно сама. Документ при этом записывать не требуется — сервер видит и незаписанные строки, только что добавленные пользователем.
Вариант 2. Структура и вызов без контекста
Подходит для обработчиков ПриИзменении колонок, где нужно что-то получить из базы по одной строке. Вызов не тащит за собой всю форму.
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ДанныеСтроки = Новый Структура("Номенклатура, Количество, Цена, Сумма");
ЗаполнитьЗначенияСвойств(ДанныеСтроки, ТекущиеДанные);
Результат = ЗаполнитьЦенуСтроки(ДанныеСтроки, Объект.Дата);
ЗаполнитьЗначенияСвойств(ТекущиеДанные, Результат);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ЗаполнитьЦенуСтроки(Знач ДанныеСтроки, Знач ДатаЦены)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, Номенклатура = &Номенклатура) КАК Цены";
Запрос.УстановитьПараметр("Дата", ДатаЦены);
Запрос.УстановитьПараметр("Номенклатура", ДанныеСтроки.Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
ДанныеСтроки.Цена = ?(Выборка.Следующий(), Выборка.Цена, 0);
ДанныеСтроки.Сумма = ДанныеСтроки.Количество * ДанныеСтроки.Цена;
Возврат ДанныеСтроки;
КонецФункции
Регистр цен в типовых конфигурациях устроен по-разному: где-то есть измерение вида или типа цен, где-то цены хранятся в другом регистре. Условие среза подстройте под свою конфигурацию.
Не меняйте ТекущиеДанные между вызовом сервера и возвратом результата в другом обработчике — к моменту возврата текущей может стать другая строка. Сохраните ссылку на строку в локальную переменную, как в примере, и заполняйте именно её.
Обработка строки на сервере и возврат результата
Сервер получил строку — дальше важно правильно отдать результат клиенту.
Параметры с «Знач» и без
Параметр без Знач при серверном вызове передаётся туда и обратно: если сервер его изменил, клиент получит изменённое значение. Параметр со Знач идёт только на сервер. Для данных строки удобнее явный возврат функцией и Знач в параметрах — видно, что именно вернулось, и данные не гоняются лишний раз.
Возврат на форму
- ✅ При вызове с контекстом правьте
Объект.Товарыпрямо на сервере — изменения придут на форму сами. - ✅ При вызове без контекста возвращайте
Структураи копируйте её в строку черезЗаполнитьЗначенияСвойств(ТекущиеДанные, Результат). - ✅ Если сервер должен добавить строку, при вызове с контекстом используйте
Объект.Товары.Добавить(). Без контекста верните данные новой строки, а добавьте её на клиенте.
Новая строка с сервера и курсор на ней
Частая задача: сервер добавляет строку, а пользователь должен сразу оказаться на ней. Верните идентификатор новой строки и установите его в ТекущаяСтрока на клиенте.
&НаКлиенте
Процедура ДобавитьУслугуДоставки(Команда)
ИдентификаторНовой = ДобавитьСтрокуНаСервере();
Элементы.Товары.ТекущаяСтрока = ИдентификаторНовой;
КонецПроцедуры
&НаСервере
Функция ДобавитьСтрокуНаСервере()
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Количество = 1;
Возврат НоваяСтрока.ПолучитьИдентификатор();
КонецФункции
Номенклатуру доставки и цену заполните по правилам своей конфигурации. Возвращать саму строку бессмысленно: на клиент она всё равно придёт вместе с данными формы, а нужен только её идентификатор.
Несколько выделенных строк
Для групповой обработки передайте массив идентификаторов одним вызовом:
&НаКлиенте
Процедура ОбнулитьСкидкуВыделенных(Команда)
Выделенные = Элементы.Товары.ВыделенныеСтроки;
Если Выделенные.Количество() = 0 Тогда
Возврат;
КонецЕсли;
ОбнулитьСкидкуНаСервере(Выделенные);
КонецПроцедуры
&НаСервере
Процедура ОбнулитьСкидкуНаСервере(Знач Идентификаторы)
Для Каждого Идентификатор Из Идентификаторы Цикл
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
Если СтрокаТЧ <> Неопределено Тогда
СтрокаТЧ.Скидка = 0;
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Колонка Скидка здесь для примера — подставьте свою.
Большие табличные части: как не тормозить форму
Медленная форма почти всегда означает лишние серверные вызовы, а не медленный код на сервере. Вот что реально помогает.
Один вызов вместо цикла вызовов
Самая частая причина тормозов — серверная функция внутри клиентского цикла по строкам. 500 строк — 500 обращений к серверу, каждое со своей сетевой задержкой. Соберите данные всех строк в Массив структур или массив идентификаторов и обработайте их на сервере за один вызов, одним запросом с параметром-списком.
Таблица значений на сервере
Внутри метода &НаСервере табличную часть формы можно превратить в ТаблицаЗначений: Объект.Товары.Выгрузить(). С таблицей значений удобно группировать, сворачивать и передавать её в запрос как параметр. Обратно — Объект.Товары.Загрузить(Таблица).
Если нужен прикладной объект целиком — с его методами из модуля объекта, — используйте РеквизитФормыВЗначение("Объект"), а после изменений ЗначениеВРеквизитФормы(ДокументОбъект, "Объект").
Временное хранилище
Когда сервер готовит большой набор данных, а клиенту нужен только доступ к нему позже, положите результат в ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор) и передайте на клиент адрес — строку. Тот же адрес клиент потом отдаёт другому серверному вызову или фоновому заданию, а данные достают через ПолучитьИзВременногоХранилища(Адрес). Второй параметр — уникальный идентификатор формы: данные живут, пока открыта форма.
Как найти лишние вызовы
Запустите форму в отладке и включите в Конфигураторе Отладка → Замер производительности. В результатах замера видно, какие строки модуля выполнялись чаще всего и сколько времени заняли. Строки с вызовом серверной функции в клиентском цикле сразу всплывают наверх.
Кеширующий общий модуль (повторное использование «На время сеанса») не подходит для данных, которые меняются: цена, остаток. Пользователь увидит старое значение до перезапуска сеанса.
Ошибки при передаче строк ТЧ и что с ними делать
Ниже симптомы, которые встречаются при отладке такого кода, и их причины.
&НаСервереБезКонтекста, а внутри есть обращение к Объект, Элементы или реквизиту формы. Либо передайте нужные значения параметрами, либо смените директиву на &НаСервере.ТекущиеДанные равно Неопределено: пользователь не выбрал строку или обработчик сработал при пустой таблице. Добавьте проверку на Неопределено до обращения к полям.НомерСтроки или индекс вместо идентификатора. Берите Элементы.Товары.ТекущаяСтрока или СтрокаТЧ.ПолучитьИдентификатор().ЗаполнитьЗначенияСвойств(ТекущиеДанные, Результат).ТекущиеДанные, элемент формы, коллекция данных формы. Скопируйте нужные поля в Структура.ПриИзменении колонки стоит контекстный вызов, и вся табличная часть каждый раз уходит на сервер. Переведите расчёт на &НаСервереБезКонтекста или считайте простую арифметику прямо на клиенте.Последний пункт часто упускают: умножить количество на цену можно в процедуре &НаКлиенте, без сервера вовсе. Сервер нужен, только когда требуются данные из базы.
Частые вопросы
Можно ли передать на сервер всю табличную часть, а не одну строку?
&НаСервере табличная часть уже доступна как Объект.Товары. Для вызова без контекста соберите строки в массив структур на клиенте.Нужно ли записывать документ, чтобы сервер увидел новую строку?
Чем `ТекущаяСтрока` отличается от `ТекущиеДанные`?
ТекущаяСтрока — идентификатор строки, число. Его можно передать на сервер. ТекущиеДанные — сама строка данных формы, она нужна для чтения и записи полей на клиенте.