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