Как программно создать реквизиты на управляемой форме 1С, записать их в регистр сведений и настроить отбор значений?

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

При разработке механизмов дополнительных свойств и характеристик объектов часто возникает задача динамического формирования полей на управляемой форме с последующим сохранением введенных данных в регистр сведений. Разберем по шагам, как правильно создавать программные реквизиты, как обращаться к их значениям, сохранять их в базу данных и настраивать отбор подчиненных значений в выпадающих списках.

1. Как правильно обращаться к программно созданному реквизиту формы

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

Проанализируем способы получения и установки значений программных реквизитов:

  1. Через квадратные скобки контекста формы: если имя реквизита хранится в строковой переменной ИмяРеквизита, чтение и запись выполняются через конструкцию ЭтаФорма[ИмяРеквизита].
  2. Через прямое обращение к свойству формы: если имя реквизита известно заранее (например, Цвет), допустимо прямое обращение ЭтаФорма.Цвет.
  3. Ошибочные варианты: обращение через Объект.НовыйРеквизит или Элементы.ИмяРеквизита является неверным, так как Объект ссылается на прикладной объект БД, а коллекция Элементы содержит визуальные элементы управления, а не данные.

2. Программное создание реквизитов и элементов формы

Рассмотрим типовую ошибку при массовом создании реквизитов. Вызов метода ИзменитьРеквизиты() внутри цикла по выборке записей приводит к многократной и ресурсоемкой перестройке контекста формы на сервере. Все новые реквизиты необходимо формировать в едином массиве и передавать методу ИзменитьРеквизиты() один раз.

Посмотрим на пример правильной реализации серверной процедуры загрузки дополнительных свойств из регистра сведений:


&НаСервере
Процедура СоздатьРеквизитыИСвойстваНаСервере()

	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ЗначенияСвойств.Свойство КАК Свойство,
		|	ЗначенияСвойств.Свойство.Наименование КАК Наименование,
		|	ЗначенияСвойств.Значение КАК Значение,
		|	Свойства.ТипЗначения КАК ТипЗначения
		|ИЗ
		|	РегистрСведений.ЗначенияСвойствКошельков КАК ЗначенияСвойств
		|	ЛЕВОЕ СОЕДИНЕНИЕ ПланВидовХарактеристик.СвойстваКошельков КАК Свойства
		|		ПО ЗначенияСвойств.Свойство = Свойства.Ссылка
		|ГДЕ
		|	ЗначенияСвойств.Кошелек = &Кошелек";
	
	Запрос.УстановитьПараметр("Кошелек", Объект.Ссылка);
	Выборка = Запрос.Выполнить().Выбрать();

	МассивНовыхРеквизитов = Новый Массив;
	СтруктураЗначений = Новый Структура;
	МассивЭлементов = Новый Массив;

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

	// Применяем изменение структуры формы однократно
	Если МассивНовыхРеквизитов.Количество() > 0 Тогда
		ИзменитьРеквизиты(МассивНовыхРеквизитов);
	КонецЕсли;

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

КонецПроцедуры

3. Настройка отбора подчиненных значений в поле ввода

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

Когда ссылка на свойство-владелец известна на этапе генерации элементов формы, правильнее использовать свойство ПараметрыВыбора:

  1. Создаем массив параметров с помощью объекта ПараметрВыбора:
    
    МассивПараметров = Новый Массив;
    МассивПараметров.Добавить(Новый ПараметрВыбора("Отбор.Владелец", Выборка.Свойство));
    
  2. Преобразуем массив в ФиксированныйМассив и передаем свойству поля: Элемент.ПараметрыВыбора = Новый ФиксированныйМассив(МассивПараметров);

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

4. Запись введенных значений в регистр сведений

Для сохранения введенных пользователем данных в регистр сведений используем обработчик ПослеЗаписиНаСервере или отдельную серверную процедуру сохранения.

Разберем пример процедуры записи значений динамических реквизитов в регистр:


&НаСервере
Процедура СохранитьЗначенияСвойствВРегистр()

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

КонецПроцедуры

Рекомендации по надежности и производительности

  1. Именование реквизитов: никогда не используйте простое удаление пробелов из наименования (СтрЗаменить(Наименование, " ", "")), так как наличие знаков препинания, кавычек или скобок вызовет ошибку имени идентификатора. Используйте уникальные идентификаторы или коды ссылок.
  2. Инициализация формы: вызывайте процедуру создания программных полей в обработчике ПриСозданииНаСервере или ПриЧтенииСозданииНаСервере, чтобы динамические реквизиты восстанавливались при каждом открытии объекта.
← На главную