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