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