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