Как реализовать автоматическую установку флажков в дочерних узлах дерева значений в 1С?

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

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

Анализ проблемы и выбор подхода

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

Важный нюанс: Многие пытаются использовать метод РеквизитФормыВЗначение, выполнять обход в объекте ДеревоЗначений и возвращать его обратно через ЗначениеВРеквизитФормы. Это "тяжелая" операция, которая требует передачи всего объема данных с клиента на сервер и обратно. Мы разберем более эффективный способ — работу напрямую с элементами формы на стороне клиента.

Способ 1. Клиентский рекурсивный обход (рекомендуемый)

Этот метод позволяет мгновенно обновить состояние флажков без обращения к серверу. Для этого мы будем использовать объект ДанныеФормыЭлементДерева и его метод ПолучитьЭлементы(). Проанализируем реализацию по шагам.

Сначала создадим основной обработчик события ПриИзменении для нашего флажка на форме:


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

КонецПроцедуры

Теперь разберем саму процедуру обхода. Мы будем передавать в нее родительскую строку и то значение флажка, которое нужно установить всем подчиненным элементам:


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

Преимущества этого метода: он работает на клиенте, не нагружает сервер и моментально перерисовывает дерево на экране пользователя.

Способ 2. Работа через конвертацию в ДеревоЗначений (серверный)

Если логика установки флажков очень сложная и требует обращения к базе данных, используется серверный метод. Однако здесь важно не забыть про обратную синхронизацию. Разберем, почему у многих разработчиков изменения "пропадают".

Когда вы вызываете РеквизитФормыВЗначение("МаркетинговыеАкции"), система создает копию данных в виде объекта ДеревоЗначений. Все изменения, внесенные в этот объект, останутся только в памяти сервера, пока вы не вызовете обратный метод ЗначениеВРеквизитФормы. Выясним, как это выглядит в коде:


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

Дополнительные приемы для профессиональной работы

Проанализируем несколько продвинутых техник, которые сделают работу с деревом более удобной:

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

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

← На главную