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

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

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

Давайте проанализируем ситуацию, представленную на форуме, где пользователь столкнулся с проблемой отбора по предопределенному родителю в справочнике «ЦелиНоменклатуры», связанном со справочником «НаименованияОбъектов».

Исходная проблема: "Несоответствие типов" при отборе по родителю

Первоначально автор попытался реализовать отбор следующим образом в событии НачалоВыбора поля формы документа:


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

При запуске этого кода возникала ошибка «Несоответствие типов». Давайте выясним причину этой ошибки.

  1. Неправильное имя поля для отбора: В структуре отбора, создаваемой для параметра Отбор, автор указал ключ "Наименование". Однако, для отбора по родителю в иерархическом справочнике необходимо использовать имя поля "Родитель". Поле Наименование, как правило, является строковым представлением элемента, а не полем для связывания по иерархии.
  2. Несоответствие типов значений: В строке ЗначениеОтбора = Новый Структура("Наименование", СсылкаНаЦелиКомандировки); мы передаем ссылку на предопределенный элемент справочника (тип СправочникСсылка.НаименованияОбъектов) в качестве значения для поля "Наименование". Если бы поле для отбора действительно называлось "Наименование", оно, скорее всего, ожидало бы строковое значение, а не ссылку. Это классический пример несоответствия типов, когда программа ожидает один тип данных, а получает другой.

Таким образом, основная причина ошибки «Несоответствие типов» заключалась в попытке сопоставить ссылку на элемент справочника с полем "Наименование", которое либо не является полем-родителем, либо ожидает значение другого типа.

Исправляем ошибку "Несоответствие типов"

Для корректной установки отбора по родителю, нам нужно изменить ключ в структуре отбора с "Наименование" на "Родитель". Поле Родитель в иерархических справочниках 1С всегда ожидает в качестве значения ссылку на другой элемент того же справочника. Предопределенное значение, полученное с помощью ПредопределенноеЗначение("Справочник.НаименованияОбъектов.ЦелиКомандировки"), как раз имеет тип СправочникСсылка.НаименованияОбъектов, что полностью соответствует ожидаемому типу для поля Родитель.

Корректный код для формирования структуры отбора будет выглядеть так:


// ...
СсылкаНаЦелиКомандировки = ПредопределенноеЗначение("Справочник.НаименованияОбъектов.ЦелиКомандировки");

// Создаем структуру для отбора, указывая поле "Родитель"
ЗначениеОтбора = Новый Структура("Родитель", СсылкаНаЦелиКомандировки); 

// Создаем структуру параметров формы, передавая в нее отбор
ПараметрыОткрытияФормы = Новый Структура("Отбор", ЗначениеОтбора);
// ...

После этой корректировки ошибка «Несоответствие типов» должна исчезнуть, так как мы теперь передаем ссылку на справочник для поля "Родитель", которое именно такой тип и ожидает.

Возникновение новой проблемы: "выбор не делается"

После исправления ошибки типа автор столкнулся с новой проблемой: «выбор не делается.. не подставляется в форму». Код, которым автор пытался решить эту проблему, выглядел следующим образом:


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

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

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

Решение проблемы возврата выбранного значения с помощью ПроцедурыОбработки

Наиболее современный и гибкий способ обработки выбора из формы в управляемом приложении 1С:Предприятие – это использование метода ОткрытьФорму() с передачей в него имени процедуры-обработчика выбора. Эта процедура будет вызвана клиентским контекстом после закрытия формы выбора, передав в нее выбранное значение.

Разберем по шагам, как это реализовать:

  1. Шаг 1: Подготовка параметров отбора.

    Мы уже разобрались с этим шагом, исправив ошибку «Несоответствие типов». Продолжаем использовать структуру с ключом "Родитель" и значением - ссылкой на предопределенный элемент.

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

    Теперь вместо ПолучитьФорму().Открыть() мы используем ОткрытьФорму(). Четвертым параметром в этот метод передается строковое имя процедуры, которая будет вызвана после выбора. Например, "ОбработкаВыбораПослеОткрытияФормы".

    
    // Открываем форму выбора, передавая параметры отбора 
    // и строковое имя процедуры для обработки выбора после закрытия формы
    ОткрытьФорму("Справочник.НаименованияОбъектов.ФормаВыбора", ПараметрыОткрытияФормы, , , "ОбработкаВыбораПослеОткрытияФормы");
    
  3. Шаг 3: Реализация ПроцедурыОбработки.

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

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

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


&НаКлиенте
Процедура ЦельНачалоВыбора(Элемент, ДанныеВыбора, ВыборДобавлением, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    СсылкаНаЦелиКомандировки = ПредопределенноеЗначение("Справочник.НаименованияОбъектов.ЦелиКомандировки");

    // Создаем структуру для отбора по полю "Родитель"
    ЗначениеОтбора = Новый Структура("Родитель", СсылкаНаЦелиКомандировки);

    // Создаем структуру параметров формы, передавая в нее отбор
    ПараметрыОткрытияФормы = Новый Структура("Отбор", ЗначениеОтбора);

    // Открываем форму выбора, передавая параметры отбора 
    // и строковое имя процедуры для обработки выбора после закрытия формы
    ОткрытьФорму("Справочник.НаименованияОбъектов.ФормаВыбора", ПараметрыОткрытияФормы, Элемент, , "ОбработкаВыбораПослеОткрытияФормы");
КонецПроцедуры

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

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

Альтернативный подход: Отбор через динамический список с использованием ОбщегоНазначенияКлиентСервер.ДобавитьЭлементКомпоновки

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

Рассмотрим, как это можно реализовать:

  1. Шаг 1: Получаем экземпляр формы выбора.

    Используем ПолучитьФорму(), но уже без передачи параметров отбора при открытии, так как мы будем настраивать его напрямую в динамическом списке формы.

    
    СтандартнаяОбработка = Ложь;
    СсылкаНаЦелиКомандировки = ПредопределенноеЗначение("Справочник.НаименованияОбъектов.ЦелиКомандировки");
    
    ФормаВыбора = ПолучитьФорму("Справочник.НаименованияОбъектов.ФормаВыбора");
    
  2. Шаг 2: Добавляем элемент отбора в динамический список.

    Находим динамический список на форме (часто он называется Список) и обращаемся к его свойству Отбор. Затем используем метод ОбщегоНазначенияКлиентСервер.ДобавитьЭлементКомпоновки() для добавления условия отбора.

    Параметры метода ДобавитьЭлементКомпоновки:

    • Первый параметр: Объект отбора динамического списка (например, ФормаВыбора.Список.Отбор).
    • Второй параметр: Имя поля для отбора ("Родитель").
    • Третий параметр: Вид сравнения (например, ВидСравненияКомпоновкиДанных.ВИерархии, если нужно отобрать не только прямых потомков, но и всю иерархию под выбранным родителем). Если нужно только прямых потомков, можно использовать ВидСравненияКомпоновкиДанных.Равно.
    • Четвертый параметр: Значение отбора (СсылкаНаЦелиКомандировки).
    • Пятый параметр: Левое значение (обычно не используется для простых отборов).
    • Шестой параметр: Использование отбора (Истина – отбор включен).
    • Седьмой параметр: Режим отображения (РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Обычный – отображается в настройках пользователя).
    
    ОбщегоНазначенияКлиентСервер.ДобавитьЭлементКомпоновки(
        ФормаВыбора.Список.Отбор, // Отбор динамического списка
        "Родитель",             // Имя поля для отбора
        ВидСравненияКомпоновкиДанных.ВИерархии, // Вид сравнения (в иерархии)
        СсылкаНаЦелиКомандировки, // Значение отбора
        ,                       // Левое значение (не задаем)
        Истина,                 // Использование отбора (включено)
        РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Обычный // Режим отображения (обычный)
    );
    
  3. Шаг 3: Открываем форму.

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

    
    // Открываем форму, теперь с настроенным отбором
    // Для возврата значения потребуется либо модальное открытие, либо ПроцедураОбработки
    ФормаВыбора.Открыть(); 
    

Полный пример кода с использованием ДобавитьЭлементКомпоновки (без механизма возврата значения, который нужно будет добавить отдельно):


&НаКлиенте
Процедура ЦельНачалоВыбора(Элемент, ДанныеВыбора, ВыборДобавлением, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    СсылкаНаЦелиКомандировки = ПредопределенноеЗначение("Справочник.НаименованияОбъектов.ЦелиКомандировки");

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

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

    // В данном примере мы только открываем форму. 
    // Для получения выбранного значения потребуется дополнительная логика:
    // либо ФормаВыбора.ОткрытьМодально(), либо передача ПроцедурыОбработки.
    ФормаВыбора.Открыть(); 
КонецПроцедуры

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

Особенности отбора по полю "Родитель" в иерархических динамических списках

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

Это означает, что установка отбора по "Родителю" программно через Отбор структуры или интерактивно (через стандартные команды "Установить отбор и сортировку списка") может не всегда работать или давать неожиданные результаты. В таких случаях особенно полезным становится ВидСравненияКомпоновкиДанных.ВИерархии, который позволяет отобрать всю ветку иерархии, начиная с указанного родителя.

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

Общие рекомендации по работе с отборами и формами выбора

  1. Всегда проверяйте типы данных: Это критически важно для предотвращения ошибки «Несоответствие типов». Убедитесь, что тип значения, которое вы передаете в отбор (например, ссылка на справочник), соответствует типу данных поля, по которому производится отбор.

  2. Используйте отладчик: Если возникают необъяснимые ошибки или неверное поведение, отладчик — ваш лучший друг. Он поможет отследить значения переменных, проверить типы данных в нужных точках кода и понять, где именно происходит сбой.

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

  4. Корректный возврат значений: В управляемых формах предпочитайте асинхронный механизм возврата значений через ПроцедураОбработки при вызове ОткрытьФорму(). Используйте модальное открытие формы (ОткрытьФормуМодально()) только в тех случаях, когда это оправдано (например, для простых диалоговых окон).

  5. Обработка отмены выбора: Всегда предусматривайте сценарий, когда пользователь отменяет выбор в форме. Проверяйте, что ВыбранноеЗначение не равно Неопределено.

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

← На главную