К статье
Практический материал

Разработка и Конфигурация · 10 мин

Как обойти табличную часть формы в 1С

Как обойти табличную часть формы в 1С: перебор строк на клиенте и сервере, работа с ДанныеФормыКоллекция, поиск строк, типичные ошибки и примеры кода.

Табличная часть на форме документа или справочника в 1С — это не сам объект данных, а его отображение через реквизит формы типа «ДанныеФормыКоллекция». Поэтому обход строк на управляемой форме отличается от привычного перебора табличной части объекта на сервере: у строк формы нет методов объекта, зато есть идентификаторы строк, а контекст делится на клиентский и серверный.

Задача обхода возникает постоянно: подсчитать итог по колонке, заполнить признак в каждой строке, проверить дубли, найти строку по условию, собрать данные для передачи в другую систему. Способ обхода зависит от того, где выполняется код (клиент или сервер), какой объём строк и нужно ли обращаться к данным объекта целиком.

Ниже разобраны рабочие варианты перебора: прямой цикл «Для Каждого», обход через количество строк и индекс, поиск через НайтиСтроки, а также серверный обход через ДанныеФормыВЗначение. Для каждого варианта указаны условия применения, ограничения и типичные ошибки.

Что представляет собой табличная часть на форме

Когда документ открывается в управляемой форме, его табличная часть помещается в реквизит формы. Если основной реквизит формы называется «Объект», то табличная часть доступна как Объект.Товары (или другое имя табличной части). Технически это значение типа «ДанныеФормыКоллекция», а каждая строка — «ДанныеФормыЭлементКоллекции».

У такой строки есть поля табличной части (Номенклатура, Количество, Цена и т.д.) и служебное свойство ПолучитьИдентификатор(), которое используется для связи с элементом формы «Таблица». Методов объекта (например, Выгрузить() напрямую у коллекции формы нет в клиентском контексте), поэтому часть операций требует преобразования данных.

Простой обход циклом «Для Каждого»

Самый частый и безопасный способ — перебор коллекции в цикле. Он работает одинаково в клиентских и серверных процедурах формы, не требует преобразования данных и позволяет читать и изменять поля строки.

&НаКлиенте

Процедура ПересчитатьСуммы(Команда)

Для Каждого Строка Из Объект.Товары Цикл

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

КонецЦикла;

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

Если нужен номер строки, его можно взять из свойства НомерСтроки элемента коллекции — для табличных частей объекта оно заполняется автоматически. Альтернатива — завести счётчик внутри цикла. Для вывода сообщений с привязкой к строке используйте ПолучитьИдентификатор(), чтобы построить путь к данным формы.

Ограничение способа: нельзя удалять текущую строку прямо внутри цикла «Для Каждого» — это приводит к пропуску строк или ошибке. Для удаления сначала соберите строки в массив, затем удаляйте их отдельным циклом.

bsl
Для Каждого Строка Из Объект.Товары Цикл
	Если Строка.Количество = 0 Тогда
		СтрокиНаУдаление.Добавить(Строка);
	КонецЕсли;
КонецЦикла;
Для Каждого Строка Из СтрокиНаУдаление Цикл
	Объект.Товары.Удалить(Строка);
КонецЦикла;

Обход по индексу и через количество строк

Когда нужен контроль над позицией — например, обход с конца или обращение к соседним строкам — используют цикл по индексу. Коллекция поддерживает обращение по индексу от 0 до Количество() - 1.

&НаСервере

Процедура ОбходПоИндексу()

Для Инд = 0 По Объект.Товары.Количество() - 1 Цикл

Строка = Объект.Товары[Инд];

// работа со строкой

КонецЦикла;

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

Обход с конца (Количество() - 1 до 0 с шагом -1) — корректный способ удалять строки по условию без промежуточного массива, потому что удаление не сдвигает ещё не обработанные элементы. Это удобно для очистки строк с нулевым количеством или отменённых позиций.

Поиск строк по условию через НайтиСтроки

Если обход нужен не ради всех строк, а ради подмножества, эффективнее использовать метод НайтиСтроки(ПараметрыОтбора). Он принимает структуру с отбором по полям и возвращает массив строк, который затем обходится обычным циклом.

&НаКлиенте

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

Отбор = Новый Структура("ВидЦены", ВидЦеныАкция);

Найденные = Объект.Товары.НайтиСтроки(Отбор);

Для Каждого Строка Из Найденные Цикл

Строка.Скидка = 0;

КонецЦикла;

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

Отбор в НайтиСтроки работает только на равенство и только по полям, реально существующим в табличной части. Условия вида «больше», «содержит», «не равно» через НайтиСтроки не выразить — для них нужен полный перебор с проверкой внутри цикла. Также учтите, что метод ищет по точному совпадению значения, включая тип: число 0 не совпадёт с пустой ссылкой.

Порядок действий при поиске строк
  1. 1
    Сформируйте структуру отбора: Новый Структура("Поле", Значение)
  2. 2
    Вызовите Объект.ТабличнаяЧасть.НайтиСтроки(Отбор) и сохраните массив
  3. 3
    Проверьте, что массив не пуст (Найденные.Количество() > 0)
  4. 4
    Обойдите массив циклом и выполните нужные изменения
  5. 5
    При необходимости обновите связанные итоги или элементы формы

Серверный обход через ДанныеФормыВЗначение

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

&НаСервере

Процедура ЗаполнитьЦеныНаСервере()

ДокументОбъект = РеквизитФормыВЗначение("Объект");

Для Каждого Строка Из ДокументОбъект.Товары Цикл

Строка.Цена = ПолучитьЦену(Строка.Номенклатура);

КонецЦикла;

ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");

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

Здесь обход идёт по табличной части самого объекта, где доступны все серверные возможности. Но есть цена вопроса: ЗначениеВРеквизитФормы полностью пересоздаёт данные формы, из-за чего сбрасывается текущая строка таблицы на форме и пользовательские динамические настройки отображения. Если важно сохранить позицию курсора, запомните идентификатор текущей строки до преобразования и восстановите после.

Работа с текущей строкой и элементом формы «Таблица»

Иногда обходить нужно не всю коллекцию, а только выделенные пользователем строки. Для этого используется свойство ВыделенныеСтроки элемента формы, связанного с табличной частью. Оно содержит массив идентификаторов, по которым строки получают через НайтиПоИдентификатору().

&НаКлиенте

Процедура ОбработатьВыделенные(Команда)

Для Каждого ИдСтроки Из Элементы.Товары.ВыделенныеСтроки Цикл

Строка = Объект.Товары.НайтиПоИдентификатору(ИдСтроки);

Строка.Пометка = Истина;

КонецЦикла;

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

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

Сравнение вариантов обхода

Для Каждого по Объект.Товары

Универсально, работает на клиенте и сервере. Не подходит для удаления строк внутри цикла

Цикл по индексу с конца

Правильный выбор для удаления строк по условию. Код чуть менее читаемый

НайтиСтроки + обход массива

Быстро и наглядно для отбора по равенству. Не поддерживает сложные условия

ДанныеФормыВЗначение + обход объекта

Для тяжёлой серверной логики и запросов. Сбрасывает состояние формы, дорого при частом вызове

Типичные ошибки и как их избежать

Большинство проблем при обходе табличной части формы связаны не с самим циклом, а с контекстом выполнения и особенностями коллекции формы. Ниже — ошибки, которые встречаются чаще всего.

Типичные ошибки
01
Удаление строки внутри цикла «Для Каждого» — пропускаются соседние строки; удаляйте через массив или обходом с конца
02
Обращение к Объект.Товары в обработчике, где данные ещё не загружены, — проверяйте, что форма создана и объект инициализирован
03
Вызов серверной процедуры из клиентского цикла на каждую строку — десятки серверных вызовов; переносите весь обход на сервер одним вызовом
04
Отбор в НайтиСтроки по несуществующему полю — ошибка выполнения; сверяйте имя поля с метаданными табличной части
05
Путаница индекса и НомерСтроки — обращение к несуществующему элементу или неверная строка в сообщении пользователю

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

Контрольные точки

Как проверить результат

Итоги по колонкам после обхода
Норма

Суммы и количества пересчитаны во всех строках, итог подвала совпадает

Тревожный признак

Часть строк не обработана, итог не сходится — возможен пропуск при удалении в цикле

Поведение формы после серверного обхода
Норма

Текущая строка и отбор сохранены или осознанно восстановлены

Тревожный признак

Курсор сброшен на первую строку, пользователь теряет контекст

Время выполнения обхода
Норма

Форма реагирует без заметной паузы

Тревожный признак

Подвисание — внутри цикла есть запросы или серверные вызовы

FAQ

Можно ли обойти табличную часть формы на клиенте без вызова сервера?

Да. Коллекция Объект.Товары полностью доступна в клиентском контексте: чтение, изменение полей, добавление и удаление строк работают без сервера. Сервер нужен только для запросов к базе и вызова серверных методов прикладных объектов.

Как удалить из табличной части все строки с нулевым количеством?

Либо соберите такие строки в массив в первом цикле и удалите их вторым циклом через Удалить(), либо обойдите коллекцию по индексу с конца и удаляйте прямо в цикле — оба способа корректны. Удалять текущую строку внутри «Для Каждого» нельзя.

Чем НайтиСтроки отличается от полного перебора с условием?

НайтиСтроки быстрее и короче в записи, но поддерживает только отбор на равенство по существующим полям. Если условие сложное (диапазон, сравнение, проверка нескольких полей с «ИЛИ»), нужен полный перебор с проверкой внутри цикла.

Как получить номер строки табличной части в цикле?

У элемента коллекции есть свойство НомерСтроки — оно соответствует видимому номеру в форме и начинается с единицы. Если нужен порядковый счётчик независимо от номеров, заведите переменную и увеличивайте её в цикле.

Почему после ЗначениеВРеквизитФормы пропала текущая строка таблицы?

Метод пересоздаёт данные формы, и состояние элемента «Таблица» сбрасывается. Перед преобразованием сохраните Элементы.Товары.ТекущаяСтрока (идентификатор), а после — найдите строку по идентификатору или по ключевым полям и восстановите позицию.

Как обойти только выделенные пользователем строки?

Используйте Элементы.ИмяТаблицы.ВыделенныеСтроки — это массив идентификаторов. Каждую строку получайте через Объект.Товары.НайтиПоИдентификатору(Ид) и обрабатывайте в цикле.

Итог

Что важно запомнить

Базовый обход — цикл «Для Каждого» по Объект.ИмяТабличнойЧасти, он работает и на клиенте, и на сервере. Для отбора по равенству — НайтиСтроки, для удаления — массив строк или обход с конца, для тяжёлой логики — один серверный вызов с ДанныеФормыВЗначение. Не делайте запросы и серверные вызовы внутри цикла.