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