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