Как рекурсивно заполнить дерево значений на форме результатом запроса

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

Заполнение иерархических структур данных на форме — одна из часто встречающихся задач при разработке на платформе 1С:Предприятие 8.3. Часто новички сталкиваются с трудностями при попытке визуализировать результат запроса в виде дерева. Рассмотрим подробно два основных подхода: использование встроенного механизма сериализации платформы и написание собственной рекурсивной функции для заполнения дерева.

Способ 1. Использование метода ЗначениеВРеквизитФормы

Этот способ является наиболее производительным и предпочтительным, так как использует внутренние механизмы платформы. Рассмотрим алгоритм действий по шагам. Для начала нам необходимо подготовить запрос, который вернет данные в иерархическом виде. В языке запросов 1С для этого используется конструкция ИТОГИ ПО ... ИЕРАРХИЯ или просто ИТОГИ ПО с указанием нужных полей группировки. Для отладки и проверки структуры выборки подойдёт консоль запросов для 1С на управляемых формах.

Проанализируем пример кода для получения дерева сотрудников по подразделениям:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   СтруктураПредприятия.Ссылка КАК Подразделение,
    |   Пользователи.Ссылка КАК Сотрудник
    |ИЗ
    |   Справочник.СтруктураПредприятия КАК СтруктураПредприятия
    |       ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Пользователи
    |       ПО СтруктураПредприятия.Ссылка = Пользователи.Подразделение
    |ИТОГИ ПО
    |   Подразделение ИЕРАРХИЯ";

РезультатЗапроса = Запрос.Выполнить();
// Выгружаем результат в объект ДеревоЗначений
ДеревоОбъект = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

// Копируем данные из объекта в реквизит формы
ЗначениеВРеквизитФормы(ДеревоОбъект, "ДеревоПользователейНаФорме");

Важное условие для работы этого метода: структура колонок в объекте ДеревоЗначений (который мы получили из запроса) должна полностью совпадать со структурой реквизита формы ДеревоПользователейНаФорме. Разберем, что это значит:

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

Способ 2. Заполнение дерева с помощью рекурсивной функции

Иногда нам требуется более сложная логика заполнения, которую нельзя реализовать простым копированием. Например, если нужно изменить оформление строк, установить картинки в зависимости от условий или трансформировать данные "на лету". В этом случае на помощь приходит рекурсия.

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

Рассмотрим пример реализации такой функции. Допустим, у нас уже есть ДеревоЗначений (полученное из запроса) и мы хотим перенести его в реквизит формы программно:


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

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

Проанализируем ситуацию: процедура ЗаполнитьДеревоРекурсивно будет "спускаться" вниз по веткам дерева до тех пор, пока не достигнет самых нижних элементов (листьев), у которых нет подчиненных строк. После этого выполнение вернется на уровень выше и продолжится для следующей ветки.

Особенности работы с иерархией в запросах

Выясним причину, почему при использовании ИТОГИ ПО Ссылка ИЕРАРХИЯ в дереве часто появляются "дубли". Когда мы используем ключевое слово ИЕРАРХИЯ, система 1С строит дерево, где родительский элемент сам становится группировкой, а внутри него еще раз выводится он же, но уже как детальная запись.

Если вам нужно чистое дерево без дублирования элементов на разных уровнях, рассмотрите следующие варианты:

Оптимизация: "Ленивая загрузка"

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

  1. При открытии формы заполняется только первый уровень дерева.
  2. Если у элемента есть дети, ему добавляется пустая строка-пустышка.
  3. В событии элемента формы ПриРазвертывании мы перехватываем момент открытия ветки пользователем.
  4. В этот момент выполняется запрос только по подчиненным элементам этой конкретной ветки, данные загружаются, а "пустышка" удаляется.

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

Резюме

Для быстрой и простой задачи используйте метод ЗначениеВРеквизитФормы, предварительно убедившись, что колонки запроса и реквизита формы идентичны. Если же требуется гибкость, используйте рекурсивный обход. Помните, что правильный выбор режима обхода результата запроса (ПоГруппировкам или ПоГруппировкамСИерархией) — это 50% успеха в правильном отображении дерева.

← На главную