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