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

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

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

Разница между ДеревомЗначений и коллекцией ДанныеФормыДерево

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

У объекта ДанныеФормыДерево нет свойства Строки и нет метода НайтиСтроки(). Для обхода и получения дочерних узлов используется метод ПолучитьЭлементы().

Если требуется применить методы классической коллекции ДеревоЗначений на сервере, необходимо предварительно выполнить преобразование:


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

Каскадная установка флажков в дереве на клиенте

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

Рассмотрим по шагам реализацию события ПриИзменении для флажка в дереве:

  1. В обработчике события получаем текущую строку таблицы формы.
  2. Вызываем рекурсивную процедуру, которая обходит всех потомков текущего узла и присваивает им значение родительского флажка.

Посмотрим на пример кода:


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

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

Сбор отмеченных элементов и передача данных в документ

Когда пользователь отметил нужные группы и элементы, необходимо собрать все отмеченные строки (только конечные элементы, без групп) в массив и передать его в форму-приемник.

Разберем рекурсивную функцию сбора отмеченных элементов и команду подтверждения выбора:


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

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

Обработка выбора в документе-приемнике

Проанализируем, почему может не срабатывать процедура ОбработкаВыбора в принимающей форме. Основные причины:

  1. Не передан владелец при открытии формы подбора: Метод ОповеститьОВыборе передает результат только объекту-владельцу. Если форма подбора открывалась вызовом ОткрытьФорму() без указания владельца, оповещение не дойдет.
  2. Несовпадение контекста: Если обработчик события назначен элементу формы (таблице Элементы.Маршрут), то при открытии подбора владельцем следует указывать именно таблицу формы: Элементы.Маршрут. Если в качестве владельца указана форма (ЭтаФорма), событие выбора должно обрабатываться в процедуре ОбработкаВыбора самой формы.

Посмотрим на пример правильного открытия подбора и приема массива выбранных строк:


// В форме документа: открытие формы подбора
&НаКлиенте
Процедура МаршрутПодбор(Команда)
    ПараметрыПодбора = Новый Структура;
    // Указываем в качестве владельца элемент формы (таблицу "Маршрут")
    ОткрытьФорму("Обработка.ПодборАдресатов.Форма.ФормаПодбораАдресатов", 
                 ПараметрыПодбора, 
                 Элементы.Маршрут);
КонецПроцедуры

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

Особенности работы со свойством ОтборСтрок у таблицы формы

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

Важно: ОтборСтрок является свойством таблицы формы, а не методом. Попытка вызова ОтборСтрок() приводит к ошибке «Метод объекта не обнаружен». Для установки отбора свойству присваивается объект ФиксированнаяСтруктура:


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