Как перебрать реквизит табличной части типа Список значений и почему возникает ошибка итератора

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Разбор причины ошибки: Почему цикл не работает?

Для начала выясним причину возникновения ошибки Итератор для значения не определен. В языке 1С эта ошибка однозначно указывает на то, что объект, к которому мы применяем цикл Для каждого, не является коллекцией. Коллекциями являются массивы, таблицы значений, структуры, соответствия и, конечно, объекты типа СписокЗначений.

Если программный код выглядит следующим образом:


Для Каждого Строка Из Объект.Товары Цикл
    Для каждого Элемент Из Строка.НарушенноеТребование Цикл
        // Здесь происходит ошибка
    КонецЦикла;
КонецЦикла;

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

Особенности отображения в отладчике и «прыгающие» типы

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

  1. Визуальная подмена: В управляемых формах платформа может использовать стандартные выпадающие списки выбора для полей с составным типом данных. Когда фокус находится на ячейке, система подготавливает интерфейс для выбора одного из значений, что ошибочно принимается за наличие коллекции внутри ячейки.
  2. Кэширование в Табло: Отладчик в управляемом приложении работает через контекст сервера или клиента. При первом обращении он может показывать метаданные поля (которое может быть настроено как список выбора), но при детальном вычислении выражения Строка.НарушенноеТребование он обращается к фактическому значению, хранящемуся в памяти. Если там записана ссылка на справочник — тип моментально «уточняется».
  3. Составной тип данных: Если реквизиту назначен составной тип, то при нажатии на кнопку выбора открывается окно выбора типа, а затем — списка значений для выбора. Пользователь видит «список», но результатом выбора всегда будет одно конкретное значение выбранного типа.

Способ 1: Использование виртуальных реквизитов формы

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

Рассмотрим пошагово, как реализовать хранение списка значений в строке таблицы:

  1. В редакторе формы добавим в существующую таблицу новый реквизит. Важно: добавляем его не в Объект.ИмяТаблицы (в метаданные), а именно в список Реквизиты формы внутри данной таблицы.
  2. Установим для этого реквизита тип СписокЗначений.
  3. Поскольку этот реквизит является виртуальным, он не сохраняется в базе данных автоматически. Нам потребуется написать процедуры для его заполнения при открытии формы и для сохранения данных в скрытое строковое поле или подчиненную таблицу при записи.

Посмотрим на пример процедуры заполнения такого списка, которая выполняется &НаКлиенте:


&НаКлиенте
Процедура ЗаполнитьСпискиВТаблице()
    Для Каждого СтрокаТЧ Из Объект.ИмяТаблицы Цикл
        // Предположим, данные для списка хранятся в другой таблице
        СтрокиДанных = Объект.ПодчиненнаяТаблица.НайтиСтроки(Новый Структура("КлючСвязи", СтрокаТЧ.Идентификатор));
        
        // Очищаем текущий визуальный список
        СтрокаТЧ.НарушенноеТребование.Очистить();
        
        Для Каждого ТекДанные Из СтрокиДанных Цикл
            // Добавляем значения в СписокЗначений в строке формы
            СтрокаТЧ.НарушенноеТребование.Добавить(ТекДанные.Значение);
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

Способ 2: Работа с составом типов и списком выбора

Если задача состоит не в хранении множества значений, а в предоставлении пользователю возможности выбрать одно значение из заранее подготовленного списка, то нам не нужен тип СписокЗначений для самого реквизита. Выясним, как правильно настроить СписокВыбора.

Проанализируем ситуацию: реквизит имеет тип СправочникСсылка.Требования. Мы хотим, чтобы при нажатии на ячейку появлялся ограниченный набор значений. Для этого используем событие ПриНачалеВыбора:


&НаКлиенте
Процедура ТаблицаНарушенноеТребованиеПриНачалеВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь; // Отключаем стандартное окно справочника
    
    Список = Новый СписокЗначений;
    Список.Добавить(ПредопределенноеЗначение("Справочник.Требования.Требование1"));
    Список.Добавить(ПредопределенноеЗначение("Справочник.Требования.Требование2"));
    
    // Показываем список пользователю для выбора
    ВыбранныйЭлемент = ВыбратьИзСписка(Список, Элемент);
    
    Если ВыбранныйЭлемент <> Неопределено Тогда
        Объект.ИмяТаблицы[Элементы.ИмяТаблицы.ТекущаяСтрока].НарушенноеТребование = ВыбранныйЭлемент.Значение;
    КонецЕсли;
КонецПроцедуры

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

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

Чтобы код был отказоустойчивым, перед запуском цикла по реквизиту, тип которого вызывает сомнения, следует программно проверить его тип. Проанализируем использование функции ТипЗнч():


Для Каждого Строка Из Объект.ИмяТаблицы Цикл
    ТекущееПоле = Строка.НарушенноеТребование;
    
    Если ТипЗнч(ТекущееПоле) = Тип("СписокЗначений") Тогда
        // Безопасный перебор коллекции
        Для Каждого ЭлементСписка Из ТекущееПоле Цикл
            Сообщить(ЭлементСписка.Значение);
        КонецЦикла;
    ИначеЕсли ЗначениеЗаполнено(ТекущееПоле) Тогда
        // Обработка как одиночного значения
        Сообщить("Это ссылка: " + ТекущееПоле);
    КонецЕсли;
КонецЦикла;

Резюме и рекомендации

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

Основные выводы:

Используя эти методы, вы сможете корректно обрабатывать данные в табличных частях и избегать ошибок при работе с динамически формируемыми коллекциями в управляемом интерфейсе 1С.

← На главную