При разработке интерфейсов форм в 1С часто возникает задача организации каскадного выбора, когда значения зависимых полей должны строго соответствовать иерархии элементов справочника. Например, в форме необходимо последовательно выбрать тип продукции, затем соответствующий ему вид продукции и далее подвид. При этом, пока вышестоящий уровень не выбран, выбор дочерних уровней должен быть полностью недоступен.
Рассмотрим особенности работы платформы в таких сценариях, выясним причину некорректного поведения стандартных механизмов и разберем правильные способы реализации задачи.
Проанализируем типовой подход разработчиков без использования программного кода. В свойствах реквизита или элемента формы настраивается свойство Связи параметров выбора с отбором вида Отбор.Родитель(ТипПродукции).
На практике при таком подходе возникает проблема: если поле ТипПродукции еще не заполнено, оно содержит значение ПустаяСсылка. В иерархическом справочнике элементы верхнего (корневого) уровня не имеют родителя, то есть их реквизит Родитель равен пустой ссылке. В результате платформа честно применяет отбор Родитель = ПустаяСсылка и выводит в зависимом поле элементы первого уровня вместо пустого списка.
Полностью отказаться от программного кода в данном сценарии не удастся, так как декларативные настройки платформы не предназначены для блокировки выпадающих списков при незаполненных параметрах отбора. Разберем рабочие и надежные варианты решения этой задачи.
Наиболее удобный и наглядный для пользователя способ — динамически управлять доступностью зависимых элементов. Пока родительское поле пустое, поле ввода подчиненного уровня блокируется для редактирования.
Разберем пошаговую реализацию этого механизма:
ПриСозданииНаСервере или ПриЧтенииНаСервере инициализируем начальное состояние элементов формы в зависимости от заполненности данных:
&НаСервере
Процедура УстановитьДоступностьЭлементовНаСервере()
ТипЗаполнен = ЗначениеЗаполнено(Запись.ТипПродукции);
ВидЗаполнен = ЗначениеЗаполнено(Запись.ВидПродукции);
Элементы.ВидПродукции.Доступность = ТипЗаполнен;
Элементы.ПодвидПродукции.Доступность = ТипЗаполнен И ВидЗаполнен;
КонецПроцедуры
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
УстановитьДоступностьЭлементовНаСервере();
КонецПроцедуры
ПриИзменении вышестоящих полей на клиенте очищаем зависимые реквизиты при смене родителя и переключаем свойство Доступность:
&НаКлиенте
Процедура ТипПродукцииПриИзменении(Элемент)
ТипЗаполнен = ЗначениеЗаполнено(Запись.ТипПродукции);
Если Не ТипЗаполнен Тогда
Запись.ВидПродукции = ПредопределенноеЗначение("Справочник.ПродукцияКомпании.ПустаяСсылка");
Запись.ПодвидПродукции = ПредопределенноеЗначение("Справочник.ПродукцияКомпании.ПустаяСсылка");
КонецЕсли;
Элементы.ВидПродукции.Доступность = ТипЗаполнен;
Элементы.ПодвидПродукции.Доступность = ТипЗаполнен И ЗначениеЗаполнено(Запись.ВидПродукции);
КонецПроцедуры
&НаКлиенте
Процедура ВидПродукцииПриИзменении(Элемент)
ВидЗаполнен = ЗначениеЗаполнено(Запись.ВидПродукции);
Если Не ВидЗаполнен Тогда
Запись.ПодвидПродукции = ПредопределенноеЗначение("Справочник.ПродукцияКомпании.ПустаяСсылка");
КонецЕсли;
Элементы.ПодвидПродукции.Доступность = ЗначениеЗаполнено(Запись.ТипПродукции) И ВидЗаполнен;
КонецПроцедуры
Если требуется оставить поле визуально доступным, но запретить открывать окно выбора или выпадающий список до момента заполнения родительского поля, используем обработчик НачалоВыбора.
Посмотрим на пример обработки события для зависимого поля:
&НаКлиенте
Процедура ВидПродукцииНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
Если Не ЗначениеЗаполнено(Запись.ТипПродукции) Тогда
СтандартнаяОбработка = Ложь;
ПоказатьПредупреждение(, "Сначала необходимо выбрать тип продукции!");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПодвидПродукцииНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
Если Не ЗначениеЗаполнено(Запись.ВидПродукции) Тогда
СтандартнаяОбработка = Ложь;
ПоказатьПредупреждение(, "Сначала необходимо выбрать вид продукции!");
КонецЕсли;
КонецПроцедуры
При таком подходе установка параметра СтандартнаяОбработка = Ложь прерывает открытие формы выбора и выпадающего списка, предотвращая отображение элементов верхнего уровня.
С точки зрения архитектуры данных дублирование всех уровней иерархии в ресурсах регистра создает избыточность. Если элемент нижнего уровня (подвид) однозначно определяет своего родителя (вид) и прародителя (тип), более надежным решением является выбор только конечного элемента.
Рассмотрим, как организовать такую схему:
ПодвидПродукции.ТипПродукции и ВидПродукции на форме делаются доступными только для чтения (свойство ТолькоПросмотр = Истина).ПриИзменении поля подвида автоматически определяются и подставляются его вышестоящие родители:
&НаКлиенте
Процедура ПодвидПродукцииПриИзменении(Элемент)
ЗаполнитьРодителейНаСервере();
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьРодителейНаСервере()
Если ЗначениеЗаполнено(Запись.ПодвидПродукции) Тогда
РодительВид = Запись.ПодвидПродукции.Родитель;
Запись.ВидПродукции = РодительВид;
Если ЗначениеЗаполнено(РодительВид) Тогда
Запись.ТипПродукции = РодительВид.Родитель;
Иначе
Запись.ТипПродукции = ПредопределенноеЗначение("Справочник.ПродукцияКомпании.ПустаяСсылка");
КонецЕсли;
Иначе
Запись.ВидПродукции = ПредопределенноеЗначение("Справочник.ПродукцияКомпании.ПустаяСсылка");
Запись.ТипПродукции = ПредопределенноеЗначение("Справочник.ПродукцияКомпании.ПустаяСсылка");
КонецЕсли;
КонецПроцедуры
Такой подход исключает ошибки расхождения данных, когда выбранный вид продукции не соответствует типу, и существенно упрощает ввод данных для конечного пользователя.