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