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

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

Как перебрать строки таблицы формы в 1С

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

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

На практике применяются три подхода: перебор коллекции формы через Для Каждого, перебор по индексу через Количество() и обход на сервере через РеквизитФормыВЗначение. Выбор зависит от того, где выполняется код (клиент или сервер), нужно ли менять строки и насколько велика таблица.

Где именно лежат строки таблицы на форме

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

У коллекции формы есть методы Количество(), Получить() (обращение по индексу), НайтиСтроки(), НайтиПоИдентификатору(), Индекс(), а также Добавить(), Удалить(), Сдвинуть(). Каждая строка — объект ДанныеФормыЭлементКоллекции, у которого доступны поля по именам колонок и служебные свойства вроде ПолучитьИдентификатор().

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

Базовый перебор циклом «Для Каждого»

Самый читаемый способ — цикл Для Каждого Строка Из Объект.Товары Цикл. Внутри цикла обращайтесь к реквизитам строки по именам колонок: Строка.Количество, Строка.Цена и так далее. Такой код работает и на клиенте (в директиве &НаКлиенте), и на сервере.

&НаКлиенте

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

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

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

КонецЦикла;

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

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

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

Перебор по индексу и удаление строк

Когда требуется знать номер строки или удалять элементы, перебирайте коллекцию по индексу через Количество() и Получить(). Индексация начинается с нуля, поэтому последняя строка имеет индекс Количество() - 1.

&НаКлиенте

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

ВГраница = Объект.Товары.Количество() - 1;

Для Инд = 0 По ВГраница Цикл

Строка = Объект.Товары.Получить(ВГраница - Инд);

Если Не ЗначениеЗаполнено(Строка.Номенклатура) Тогда

Объект.Товары.Удалить(Строка);

КонецЕсли;

КонецЦикла;

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

Обратите внимание на приём: обход идёт с конца (ВГраница - Инд). При удалении с начала индексы оставшихся строк сдвигаются, и цикл пропускает элементы. Обход с конца — стандартный безопасный паттерн для удаления по индексу.

Поиск нужных строк через НайтиСтроки

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

&НаКлиенте

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

Отбор = Новый Структура("ГруппаТоваров", ТекущаяГруппа);

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

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

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

КонецЦикла;

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

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

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

Перебор на сервере через РеквизитФормыВЗначение

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

&НаСервере

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

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

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

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

КонецЦикла;

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

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

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

Сравнение вариантов перебора

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

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

По индексу с конца

Нужен при удалении строк или когда важна позиция. Код чуть громоздче.

НайтиСтроки с отбором

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

РеквизитФормыВЗначение на сервере

Для тяжёлых расчётов и запросов. Требует обратной конвертации и даёт накладные расходы.

Работа с текущей строкой и выделенными строками

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

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

&НаКлиенте

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

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

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

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

КонецЦикла;

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

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

Первая ошибка — попытка вызвать серверные методы объекта на клиенте. На клиенте Объект.Товары — это данные формы, у неё нет, например, метода Выгрузить() в полном смысле; для выгрузки в таблицу значений нужен серверный вызов. Вторая ошибка — обращение к Элементы.Товары.ТекущаяСтрока как к данным: ТекущаяСтрока возвращает идентификатор, а не строку с полями.

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

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

Проверка результата после перебора

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

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

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

Пустая таблица
Норма

Цикл завершается без ошибок, ничего не происходит

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

Ошибка обращения по индексу 0 при Количество()=0

Удаление строк
Норма

Удалены все целевые строки, остальные на месте

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

Часть строк пропущена — признак удаления в прямом цикле

Серверная обработка
Норма

После процедуры данные на форме обновились

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

Изменений не видно — не выполнено ЗначениеВРеквизитФормы

Итог

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

Перебирайте строки циклом Для Каждого по Объект.ТабличнаяЧасть, удаляйте обходом с конца или через массив, ищите через НайтиСтроки, тяжёлую обработку выносите на сервер с обратной конвертацией реквизита формы. Идентификатор строки не равен её индексу.

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

Как перебрать строки табличной части в обычной (неуправляемой) форме?

В обычных формах табличная часть доступна напрямую как коллекция объекта: цикл Для Каждого Строка Из Товары Цикл работает без прослоек данных формы. Обращение по индексу — через квадратные скобки Товары[Инд].

Как узнать номер строки внутри цикла Для Каждого?

Используйте свойство Строка.НомерСтроки — оно поддерживается платформой и отражает видимый номер. Либо перебирайте по индексу и прибавляйте единицу: номер = индекс + 1.

Можно ли перебрать строки динамического списка на форме?

Нет, динамический список не является коллекцией данных — это механизм отображения результата запроса. Его строки нельзя перебрать циклом. Для обработки данных выполните отдельный запрос с тем же отбором на сервере.

Почему НайтиСтроки возвращает пустой массив, хотя строка есть?

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

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

Точно так же: реквизит формы типа «ТаблицаЗначений» на управляемой форме представлен как ДанныеФормыКоллекция. Перебирайте его циклом Для Каждого Строка Из ИмяРеквизита Цикл — синтаксис идентичен табличной части.

Что быстрее: Для Каждого или перебор по индексу?

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