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