Как скрыть пустые группы при установке отбора в форме выбора справочника 1С?

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

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

Почему стандартный отбор динамического списка не скрывает пустые группы?

Проанализируем архитектурную причину поведения динамического списка. В управляемом приложении при режимах отображения «Иерархический список» или «Дерево» отборы по реквизитам элементов принципиально не исключают группы справочника из видимости.

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

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


ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Активный", Истина);

ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Отбор", ПараметрыОтбора);

ОткрытьФорму("Справочник.Номенклатура.Форма.ФормаВыбора", ПараметрыФормы, Элемент);

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

Способ 1. Переключение динамического списка в плоский режим

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

Переведем таблицу динамического списка в режим плоского списка без иерархии и исключим отображение самих групп с помощью отбора по служебному полю ЭтоГруппа:

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

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

При таком подходе пользователь видит сплошной список всех подходящих элементов без пустых строк и без промежуточных пустых папок.

Способ 2. Программное условное оформление для исключения пустых групп

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

Разберем по шагам реализацию данного алгоритма в модуле формы выбора:

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

&НаСервере
Процедура СкрытьПустыеГруппыНаСервере()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗРЕШЕННЫЕ
        |   Спр.Ссылка КАК Ссылка
        |ИЗ
        |   Справочник.Номенклатура КАК Спр
        |ГДЕ
        |   Спр.ЭтоГруппа = ИСТИНА
        |   И НЕ Спр.Ссылка В
        |       (ВЫБРАТЬ РАЗЛИЧНЫЕ
        |           Элементы.Родитель
        |       ИЗ
        |           Справочник.Номенклатура КАК Элементы
        |       ГДЕ
        |           Элементы.ЭтоГруппа = ЛОЖЬ
        |           И Элементы.Активный = ИСТИНА)";
    
    Результат = Запрос.Выполнить();
    МассивПустыхГрупп = Результат.Выгрузить().ВыгрузитьКолонку("Ссылка");
    
    Если МассивПустыхГрупп.Количество() > 0 Тогда
        
        ЭлементОформления = Список.УсловноеОформление.Элементы.Добавить();
        
        // Настраиваем отбор по пустым группам
        ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
        ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
        ЭлементОтбора.ПравоеЗначение = МассивПустыхГрупп;
        ЭлементОтбора.Использование = Истина;
        
        // Устанавливаем скрытие строк
        ЭлементОформления.Оформление.УстановитьЗначениеПараметра("Видимость", Ложь);
        
    КонецЕсли;
    
КонецПроцедуры

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

Способ 3. Разделение формы на дерево групп и список элементов

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

Организуем структуру формы следующим образом:

  1. Слева размещаем элемент формы типа ДеревоЗначений, куда запросом единоразово выгружаются только те узлы, у которых есть подчиненные элементы с признаком Активный = Истина.
  2. Справа размещаем динамический список элементов в режиме отображения Список.
  3. При активизации строки дерева значений слева накладываем отбор на динамический список справа по полю Родитель:

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

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

Способ 4. Замена динамического списка деревом значений

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

Сформируем дерево на сервере одним запросом с итогами по иерархии:


&НаСервере
Процедура ЗаполнитьДеревоВыбораНаСервере()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Спр.Ссылка КАК Ссылка,
        |   Спр.Наименование КАК Наименование,
        |   Спр.ЭтоГруппа КАК ЭтоГруппа
        |ИЗ
        |   Справочник.Номенклатура КАК Спр
        |ГДЕ
        |   Спр.Активный = ИСТИНА
        |   И Спр.ЭтоГруппа = ЛОЖЬ
        |ИТОГИ ПО
        |   Ссылка ТОЛЬКО ИЕРАРХИЯ";
        
    ДеревоРезультат = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
    ЗначениеВРеквизитФормы(ДеревоРезультат, "ДеревоВыбора");
    
КонецПроцедуры

При использовании конструкции ИТОГИ ПО Ссылка ТОЛЬКО ИЕРАРХИЯ запрос автоматически выстроит только те родительские ветки, внутри которых присутствуют записи, удовлетворяющие условию секции ГДЕ. Все остальные неактуальные группы вообще не попадут в результирующую структуру данных таблицы.

← На главную