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