Как в управляемой форме 1С отдать серверу текущую строку табличной части
Содержание
Чтобы передать текущую строку табличной части на сервер в 1С 8.3, возьмите на клиенте её идентификатор Элементы.Товары.ТекущаяСтрока и передайте его в процедуру с директивой &НаСервере. Там строку находят методом Объект.Товары.НайтиПоИдентификатору(Идентификатор). Если серверу нужна не форма, а только пара значений из строки, соберите их в Структура и вызовите процедуру &НаСервереБезКонтекста.
Сам объект ТекущиеДанные на сервер передавать не нужно: это свойство живёт только на клиенте. Всё, что описано ниже, относится к управляемым формам платформы 8.3. В обычных формах толстого клиента разделения на клиент и сервер в модуле формы нет, и строку берут напрямую из ЭлементыФормы.Товары.ТекущаяСтрока.
Где живёт строка табличной части: клиент, сервер и данные формы
В управляемой форме табличная часть документа — это не ТабличнаяЧасть объекта, а её копия в данных формы. Тип реквизита Объект.Товары — ДанныеФормыКоллекция, тип отдельной строки — ДанныеФормыЭлементКоллекции.
На экране эту коллекцию показывает элемент ТаблицаФормы. У него три свойства, через которые вы узнаёте, на какой строке стоит пользователь:
| Свойство | Что возвращает | Где доступно |
|---|---|---|
ТекущиеДанные | Саму строку (ДанныеФормыЭлементКоллекции) или Неопределено | Только клиент |
ТекущаяСтрока | Идентификатор строки (число) или Неопределено | Клиент и сервер |
ВыделенныеСтроки | Массив идентификаторов выделенных строк | Клиент и сервер |
Отсюда главное правило. 🔑 Через границу клиент-сервер передают идентификатор или значения полей, а не элемент коллекции.
Почему не элемент целиком:
- 🧩 при вызове
&НаСервереданные формы и так уходят на сервер вместе с контекстом — строка там уже есть, её нужно только найти; - 🔁 переданный параметром элемент на сервере стал бы копией: изменения в нём не вернутся в таблицу на экране;
- 📦 в
&НаСервереБезКонтекстаданных формы нет вообще, туда идут только значения простых типов, ссылки и универсальные коллекции.
Идентификатор строки — не номер строки. Номер (НомерСтроки) меняется при сортировке и удалении. Идентификатор привязан к самой строке, пока коллекция не перезаполнена целиком.
Если форма обычная, а не управляемая
В конфигурациях на обычных формах (толстый клиент, старые доработки) код модуля формы выполняется там же, где форма, — на клиенте толстого клиента. Директив &НаСервере в модуле обычной формы нет.
Текущую строку там получают так: ЭлементыФормы.Товары.ТекущаяСтрока возвращает саму строку табличной части, а ЭлементыФормы.Товары.ТекущиеДанные — данные строки. С ними работают напрямую, без поиска по идентификатору.
Когда код из обычной формы обращается к серверному общему модулю (с флагом «Вызов сервера»), правило то же, что и в управляемой форме:
- 📌 передавайте значения полей или ссылку на документ;
- 📌 результат возвращайте функцией и записывайте в строку на стороне формы.
Если базу переводят на управляемые формы, именно места с прямой работой со строкой переписывают первыми: ТекущаяСтрока в новой форме — уже число, а не строка табличной части.
Передача текущей строки по идентификатору в процедуру &НаСервере
Этот способ нужен, когда серверу требуется работать с данными формы: пересчитать строку, заполнить её из запроса, проверить остатки по реквизитам шапки.
Клиентская часть
&НаКлиенте
Процедура ПересчитатьСтроку(Команда)
Идентификатор = Элементы.Товары.ТекущаяСтрока;
Если Идентификатор = Неопределено Тогда
Сообщить("Выберите строку в таблице.");
Возврат;
КонецЕсли;
ПересчитатьСтрокуНаСервере(Идентификатор);
КонецПроцедуры
Серверная часть
&НаСервере
Процедура ПересчитатьСтрокуНаСервере(Идентификатор)
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
Если СтрокаТЧ = Неопределено Тогда
Возврат;
КонецЕсли;
СтрокаТЧ.Цена = ПолучитьЦену(СтрокаТЧ.Номенклатура, Объект.Дата);
СтрокаТЧ.Сумма = СтрокаТЧ.Цена * СтрокаТЧ.Количество;
КонецПроцедуры
После возврата с сервера данные формы синхронизируются, и пользователь сразу видит новую цену и сумму в той же строке. Функцию ПолучитьЦену здесь пишете вы сами — это обычный запрос к регистру цен.
💡 Можно не передавать идентификатор параметром, а прочитать Элементы.Товары.ТекущаяСтрока прямо на сервере. Работает, но явный параметр читается лучше: по сигнатуре видно, с какой строкой работает процедура.
Значения строки в структуре для &НаСервереБезКонтекста
Если серверу нужны только два-три поля строки, тащить туда всю форму незачем. Соберите значения в Структура на клиенте и вызовите внеконтекстную функцию.
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
ТекДанные = Элементы.Товары.ТекущиеДанные;
Если ТекДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ДанныеСтроки = Новый Структура("Номенклатура, Количество",
ТекДанные.Номенклатура, ТекДанные.Количество);
Результат = ДанныеДляСтроки(ДанныеСтроки, Объект.Дата);
ТекДанные.Цена = Результат.Цена;
ТекДанные.Сумма = Результат.Цена * ТекДанные.Количество;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ДанныеДляСтроки(ДанныеСтроки, ДатаДокумента)
Результат = Новый Структура("Цена", 0);
// здесь запрос к регистру цен по ДанныеСтроки.Номенклатура на ДатаДокумента
Возврат Результат;
КонецФункции
Обратите внимание на схему: сервер ничего не пишет в строку, он только возвращает значения. Записывает их в строку клиент — через ТекДанные.
Когда строка содержит много колонок, перечислять их вручную неудобно. Вариант: создать структуру с нужными ключами и заполнить её одной командой.
ДанныеСтроки = Новый Структура("Номенклатура, Характеристика, Количество, Цена");
ЗаполнитьЗначенияСвойств(ДанныеСтроки, ТекДанные);
ЗаполнитьЗначенияСвойств копирует только те свойства, ключи которых уже есть в структуре. Пустая Новый Структура без ключей останется пустой.
Какой способ выбрать
| Способ | Когда подходит | Минусы |
|---|---|---|
Идентификатор + &НаСервере | Нужно изменить строку или учесть реквизиты шапки и другие строки | На сервер уходят все данные формы |
Структура + &НаСервереБезКонтекста | Нужен расчёт по 2–5 полям, результат пишет клиент | Ключи структуры поддерживаете вручную |
Идентификатор + объект (РеквизитФормыВЗначение) | Нужны методы объекта документа | Самый тяжёлый вызов, идентификаторы строк обновятся |
Команды формы и параметр команды: откуда брать строку
Обработчик команды формы получает один параметр — Команда. Это описание самой команды (имя, заголовок), а не данные. Строку в нём искать бесполезно, её берут из элемента формы, как в примерах выше.
Другое дело — общая команда или команда объекта с заданным свойством ТипПараметраКоманды. Её обработчик в модуле команды выглядит так:
&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
ОбработатьНаСервере(ПараметрКоманды);
КонецПроцедуры
Сюда платформа сама подставляет текущую строку списка или формы-источника. Для формы списка это ссылка на объект. Если в свойстве РежимИспользованияПараметра выбран режим Множественный, в ПараметрКоманды придёт массив ссылок выделенных строк.
Ссылку на сервер передавать безопасно и дёшево: это простое значение. Табличную часть документа через параметр команды так не передать — команда видит ссылку на документ, а не строку внутри него.
⚠️ В динамическом списке ТекущаяСтрока — это не число, а ключ строки: обычно ссылка на элемент. ТекущиеДанные списка тоже есть, но в нём только колонки, выведенные в список. Если серверу нужен реквизит, которого нет в колонках, передайте ссылку и прочитайте реквизит на сервере.
Несколько выделенных строк: массив идентификаторов
Чтобы пользователь мог выделить несколько строк
, у элемента таблицы установите свойство РежимВыделения в значение Множественный. Дальше всё как с одной строкой, только вместо идентификатора — массив.
&НаКлиенте
Процедура УстановитьСкидку(Команда)
Выделенные = Элементы.Товары.ВыделенныеСтроки;
Если Выделенные.Количество() = 0 Тогда
Сообщить("Выделите строки для скидки.");
Возврат;
КонецЕсли;
УстановитьСкидкуНаСервере(Новый ФиксированныйМассив(Выделенные), 5);
КонецПроцедуры
&НаСервере
Процедура УстановитьСкидкуНаСервере(Идентификаторы, ПроцентСкидки)
Для Каждого Ид Из Идентификаторы Цикл
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(Ид);
Если СтрокаТЧ = Неопределено Тогда
Продолжить;
КонецЕсли;
СтрокаТЧ.ПроцентСкидки = ПроцентСкидки;
СтрокаТЧ.Сумма = СтрокаТЧ.Цена * СтрокаТЧ.Количество
* (100 - ПроцентСкидки) / 100;
КонецЦикла;
КонецПроцедуры
Пример с числами: цена 1 200, количество 3, скидка 5 %. Сумма = 1 200 × 3 × 95 / 100 = 3 420.
Зачем ФиксированныйМассив: ВыделенныеСтроки — живое свойство элемента, его содержимое меняется, когда пользователь щёлкает по таблице. Копия фиксирует набор строк на момент нажатия кнопки.
Удаление выделенных строк
При удалении идите не по массиву выделенных, а по найденным строкам, и удаляйте через коллекцию:
Для Каждого Ид Из Идентификаторы Цикл
СтрокаТЧ = Объект.Товары.НайтиПоИдентификатору(Ид);
Если СтрокаТЧ <> Неопределено Тогда
Объект.Товары.Удалить(СтрокаТЧ);
КонецЕсли;
КонецЦикла;
Цикл безопасен: перебирается массив идентификаторов, а не сама таблица. Идентификаторы оставшихся строк после удаления не меняются.
Изменить строку через объект документа и вернуть её в форму
Иногда нужны процедуры из модуля объекта: заполнение по нормативу, пересчёт серий, проверка заполнения. Тогда данные формы превращают в объект, работают с ним и кладут обратно.
&НаСервере
Процедура ЗаполнитьСтрокуОбъектом(Идентификатор)
СтрокаФормы = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
Индекс = Объект.Товары.Индекс(СтрокаФормы);
ДокОбъект = РеквизитФормыВЗначение("Объект");
СтрокаОбъекта = ДокОбъект.Товары[Индекс];
ДокОбъект.ЗаполнитьСтроку(СтрокаОбъекта); // ваш экспортный метод модуля объекта
ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
// идентификаторы после перезаполнения новые — восстанавливаем курсор
Элементы.Товары.ТекущаяСтрока = Объект.Товары[Индекс].ПолучитьИдентификатор();
КонецПроцедуры
Три приёма, без которых этот код ломается:
- Строку объекта ищут по индексу, а не по идентификатору: у
ТабличнаяЧастьобъекта нет идентификаторов формы. - После
ЗначениеВРеквизитФормыколлекция формы заполняется заново, и старый идентификатор больше не находит строку. - Курсор возвращают вручную, иначе пользователь окажется на первой строке.
Если в форме есть несохранённые изменения, они уходят в объект вместе с остальными данными — это нормально, но не вызывайте здесь запись документа без явного действия пользователя.
Ошибки при передаче строки на сервер и что с ними делать
ТекущиеДанные вернуло Неопределено. Проверяйте результат на Неопределено перед обращением к полям — особенно в обработчиках, которые срабатывают при пустой таблице.Объект.Товары, найденную через НайтиПоИдентификатору.ЗначениеВРеквизитФормы, Загрузить, Очистить). Получайте идентификатор после перезаполнения или ищите строку по индексу.&НаСервере.Элементы.Товары.ТекущаяСтрока или переданный идентификатор.ТекущаяСтрока вместо ВыделенныеСтроки, либо у таблицы одиночный режим выделения.Отдельная ловушка — обработчик ПриАктивизацииСтроки. Он срабатывает при каждом движении курсора, в том числе стрелками. Серверный вызов прямо в нём даёт заметные подвисания при листании таблицы.
Как передавать строки на сервер и не тормозить форму
Каждый вызов сервера — это сетевой обмен. В локальной сети он почти незаметен, на тонком клиенте через интернет и в веб-клиенте каждый лишний вызов ощущается.
Что реально ускоряет форму:
- ⚡ Один вызов вместо цикла. Не вызывайте сервер для каждой строки в клиентском цикле. Соберите массив идентификаторов и обработайте всё за один заход.
- 📉 БезКонтекста, где можно.
&НаСерверепередаёт данные формы туда и обратно. При таблице на 500–1000 строк разница с внеконтекстным вызовом видна без замеров. - ⏱ Отложенная реакция на курсор. В
ПриАктивизацииСтрокине вызывайте сервер напрямую: подключите обработчик ожидания на 0,1–0,2 секунды черезПодключитьОбработчикОжидания("ОбновитьПодвал", 0.2, Истина). При быстром листании сработает только последний. - 🧮 Расчёты на клиенте. Сумму из цены и количества считают на клиенте, сервер для этого не нужен.
- 🗂 Кэш неизменных данных. Если по номенклатуре нужен реквизит, который не меняется в рамках формы, держите его в реквизите формы или в соответствии на клиенте.
Как проверить, сколько вызовов идёт на сервер
Запустите базу из Конфигуратора в режиме отладки и включите замер производительности в меню отладки. Пройдите сценарий в форме и посмотрите, какие серверные процедуры вызывались и сколько раз. Директива &НаКлиентеНаСервереБезКонтекста у общих расчётных функций тоже помогает: такая функция выполняется там, откуда её вызвали, без лишнего похода на сервер.
Частые вопросы
Можно ли передать на сервер всю таблицу значений из формы?
ТаблицаЗначений на сервере превращают в обычную таблицу через РеквизитФормыВЗначение. Во внеконтекстную процедуру таблицу передают, предварительно выгрузив нужные колонки в массив структур.Чем `ТекущаяСтрока` отличается от `НомерСтроки`?
ТекущаяСтрока — внутренний идентификатор строки в данных формы. НомерСтроки — видимый номер в табличной части документа. Номер меняется при сортировке и удалении, идентификатор — нет.Как получить идентификатор строки, если у меня есть сама строка?
ПолучитьИдентификатор(). Он есть у ДанныеФормыЭлементКоллекции и у элементов дерева формы.Работает ли этот подход с деревом формы?
ДанныеФормыДерево тоже есть НайтиПоИдентификатору, а ТекущаяСтрока элемента-дерева тоже возвращает идентификатор. Разница одна: строки дерева вложены, и родителя получают через ПолучитьРодителя().Нужно ли передавать текущую строку, если сервер всё равно получает форму?
Элементы.Товары.ТекущаяСтрока. Явный параметр удобнее: одну и ту же серверную процедуру можно вызвать и для текущей строки, и для любой другой, например из цикла по выделенным.